Журнал LinuxFormat - перейти на главную

LXF95:Java EE

Материал из Linuxformat
(Различия между версиями)
Перейти к: навигация, поиск
(Новая: {{Цикл/Java EE}})
 
(uncomplete)
Строка 1: Строка 1:
 
{{Цикл/Java EE}}
 
{{Цикл/Java EE}}
 +
== А я? (кс) ==
 +
''ЧАСТЬ 6 Заинтригованы ребусом в заголовке? То ли еще будет, когда прочитаете статью – '''Александр Бабаев''' большой мастер загадывать загадки, особенно если речь заходит о Web 2.0.''
 +
 +
Когда я начинал писать эту статью в первый раз, я думал, что
 +
все достаточно просто. Сел-написал-отдал. Но сразу после
 +
этого пришлось переделывать одно приложение, которое
 +
изменило понимание и сложности, и самого Аякса. Полностью.
 +
В чем же сложность? В несоответствии. С одной стороны, Аякс
 +
позволяет приблизить логику работы интернет-приложений к обычной,
 +
«десктопной». С другой стороны – все браузеры таки разные. И это
 +
последнее «таки» местами убивает наповал. Вы думаете, что браузе-
 +
ров три? IE/Firefox/Opera? Хех. Для правильного приложения их нужно
 +
протестировать десяток. IE 5.5/6/7, Firefox 1.5/2, Opera 8/9, Safari 1.3/2/
 +
(а теперь и 3). И у каждого свой нрав.
 +
 +
=== Ладно, что же такое Аякс? ===
 +
AJAX – технология асинхронного доступа к серверу с использованием
 +
JavaScript и XML. Представим себе автомобиль. Чтобы заправить его –
 +
едем на заправку, заправляем, уезжаем. Чтобы поменять фару – едем,
 +
снимаем, ставим, уезжаем. Вот это – AJAX. А если бы Аякса не было,
 +
то для каждой такой операции нужно было бы отдавать машину в сер-
 +
вис, а из сервиса возвращалась бы другая, хоть и максимально точная
 +
копия вашей, с обновлениями (бак заправлен). Асинхронность – это
 +
вообще что-то вроде дозаправки в воздухе. То есть обновление данных
 +
без «отрыва от просмотра страницы». Давайте подробнее рассмотрим
 +
компоненты этой технологии:
 +
 +
==== A ====
 +
Асинхронность реализуется разными способами. XMLHttpRequest,
 +
фреймы, Flash. Есть и другие (те же апплеты), но они распространены
 +
меньше. Второй способ – самый «простой» для человека, знакомого
 +
с HTML. Создается ма-а-аленький iframe (одна точка) и помещается
 +
куда-нибудь далеко, чтобы не было видно. Потом при необходимости
 +
подгрузить что-либо, запрос выполняется как обычно, но с целевым
 +
фреймом – тем самым, маленьким. Во фрейм загружается страничка,
 +
на которой обычно есть скрипт, который выполняется и делает свое
 +
дело (обновляет основной фрейм). Flash – это примерно то же самое,
 +
только вместо маленького скрытого фрейма используется маленькая
 +
«флэшка».
 +
Впрочем, обычно оба эти способа используются только в случае,
 +
если не подходит «основной» – XMLHttpRequest. Это название объекта
 +
в JavaScript, который может выполнять асинхронные (и синхронные)
 +
HTTP-запросы. Например:
 +
var xmlHttpRequestValue = new XMLHttpRequest();
 +
xmlHttpRequestValue.open(“POST”, “/ajax.jsp?action=update”, true);
 +
xmlHttpRequestValue.onreadystatechange = processXmlHttpResponse;
 +
xmlHttpRequestValue.send(“Информация для пересылки серверу”);
 +
Все, запрос ушел. Сразу предупрежу, что это не рабочий код, толь-
 +
ко иллюстрация. Обратите внимание на третью строчку. Выделенное
 +
жирным – это имя функции, которая будет обрабатывать события,
 +
генерируемые во время передачи запроса, такие, как «начат прием дан-
 +
ных», «закончен прием заголовка», «закончен прием данных». Если мы
 +
хотим что-либо предпринять по окончании приема данных, эта функ-
 +
ция будет выглядеть как-то так:
 +
function processXmlHttpResponse() {
 +
if (xmlHttpRequestValue.readyState == 4) {
 +
…сделать «что-либо»…
 +
}
 +
}
 +
Число «4» как раз обозначает, что обработка завершена. Функция
 +
processXmlHttpResponse() может вызываться много раз (даже точно не
 +
определено, сколько), но как только readyState станет равным 4, мы это
 +
«поймаем» и обработаем результат.
 +
 +
==== J ====
 +
JavaScript – объектный язык. То есть создать класс, как в Java, в нем
 +
нельзя. А вот новый объект – запросто. И сформировать структуру объ-
 +
екта (прописать методы, поля) «на лету» тоже можно.
 +
Чаще всего JavaScript используется как простой способ динами-
 +
чески изменять структуру документа. Например, чтобы поменять цвет
 +
параграфа, можно выполнить такой код:
 +
document.getElementById(“paragraph”).style.color = “red”;
 +
Параграф после этого покраснеет. Чтобы понимать, как добраться до
 +
отдельных параграфов, нужно изучить DOM (Document Object Model).
 +
 +
==== X ====
 +
XML, который попал в AJAX последней буквой, используется (как уже
 +
упоминалось) далеко не всегда, но часто. Благо, в JavaScript есть воз-
 +
можности по достаточно простому преобразованию чего угодно в XML,
 +
да и в других языках (не только в Java) библиотеки по работе с этим
 +
форматом достаточно хорошо развиты.
 +
 +
=== Помощники ===
 +
Из-за того, что реализации XMLHttpRequest различаются (в паре круп-
 +
ных деталей и, что хуже, в паре десятков мелочей), самому писать
 +
кросс-браузерную обработку достаточно тяжело и долго. Поэтому
 +
давайте посмотрим на библиотеки, в состав которых входит «AJAX-
 +
подсистема» и которые сделаны либо специально для Java-северной
 +
составляющей, либо поддерживают ее.
 +
DOJO Огромная библиотека, которая много чего умеет. Если смот-
 +
реть только на коммуникативные возможности, то тут тоже все в
 +
порядке: можно использовать XMLXttpRequest, фрейм, Flash. Первый
 +
вариант предлагается по умолчанию. Чуть ниже будет пример работы
 +
с DOJO, а сейчас просто отметим, что с учетом достаточно приличной
 +
документации, логичной организации и большого количества модулей
 +
(в том числе и для использования разных методов коммуникации),
 +
библиотеку можно назвать хорошей.
 +
DWR – Direct Web Remoting. Сделана специально для Java.
 +
Достоинство состоит в том, что DWR умеет «публиковать» классы Java
 +
в JavaScript. То есть вы пишете класс в Java, настраиваете DWR (при
 +
помощи XML-дескриптора), и после запуска сервлета (обработкой
 +
запросов от DWR занимается отдельный сервлет) в JavaScript «появля-
 +
ются» методы этого класса, которые можно вызывать, так же, как если
 +
бы это были обычные функции JavaScript. Способ удобен, но навязы-
 +
вает определенный способ коммуникации. Подходит, когда не нужно
 +
контролировать каждый байт передаваемых данных и если с JavaScript
 +
вы знакомы не очень хорошо (хотя все равно его нужно знать, так как
 +
кроме коммуникации есть много чего еще).
 +
GWT – Google Web Toolkit. В свое время эта библиотека наделала
 +
много шуму. Все дело в принципе ее работы. В общем и целом, если на
 +
DWR мы пишем на Java коммуникацию, то на GWT – все приложение.
 +
Затем GWT компилирует его в клиентскую часть (HTML + JavaScript) и
 +
серверную часть (Java). То есть клиент получается автоматически (не
 +
целиком, но близко к тому). GWT берет на себя все заботы и по вза-
 +
имодействию клиента с сервером, и по созданию интерфейса (этот
 +
процесс несколько напоминает Swing), и многое другое. В принципе,
 +
достаточно хорошее решение, естественно со своими нюансами. Мы
 +
еще посмотрим на него поближе... к концу статьи.
 +
 +
=== Адресная книга с AJAX ===
 +
Настало время потренироваться. Давайте возьмем нашу адресную
 +
книгу и сделаем так, чтобы показывалась страничка с табличкой (спис-
 +
ком телефонов), а по нажатии на ссылки вместо перехода на новые
 +
странички менялся сам список: удалялись и добавлялись строки и так
 +
далее. Использовать будем DOJO. Идеология от этого не пострадает, но
 +
станет немного проще, короче и понятнее.
 +
 +
==== HTML/JavaScript ====
 +
Вот строка HTML-таблицы, из которой видно, как будет выводиться
 +
список телефонов.
 +
<tr id=”1232323” >
 +
<td>1232323</td><td>Vasya Beanov</td><td><td>…</td><td><a
 +
href=”#” on click=”remove(‘1232323’); return false;”>Remove</a> / <a
 +
href=”#” on click=”startEdit(‘1232323’); return false;”>Edit</a></td>
 +
</tr>
 +
Обратите внимание на id=”…”. Это своеобразные метки, которые
 +
позволят при необходимости найти нужную строку. Теперь рассмот-
 +
рим JavaScript-код, который выполняется при нажатии на «кнопку» Edit
 +
(как самую сложную).
 +
function startEdit(aPhone) {
 +
getCell(aPhone, 0).innerHTML = ‘<input type=”text” value =”’ +
 +
getCell(aPhone, 0).innerHTML + ‘” id =”phone_’ + aPhone + ‘”/>’;
 +
getCell(aPhone, 1).innerHTML = ‘<input type=”text” value = “’ +
 +
getCell(aPhone, 1).innerHTML + ‘” id = “name_’ + aPhone + ‘”/>’;
 +
getCell(aPhone, 2).innerHTML = ‘<input type = “text” value =”’ +
 +
getCell(aPhone, 2).innerHTML + ‘” id =”comment_’ + aPhone + ‘”/>’;
 +
getCell(aPhone, 3).innerHTML = ‘<a href = “#” onclick = “submitEdit(\’’
 +
+ aPhone + ‘\’); return false;”>Save changes</a>’;
 +
}
 +
Простым русским языком этом можно выразить так: ячейки с текс-
 +
том заменяются на ячейки с полями ввода.
 +
После редактирования и нажатия на кнопку Save changes данные
 +
отсылаются на сервер, и в ячейках снова прописывается текст.
 +
function submitEdit(aPhone) {
 +
getCell(aPhone, 0).innerHTML = document.getElementById(‘phone_’ +
 +
aPhone).value;
 +
getCell(aPhone, 1).innerHTML = document.getElementById(‘name_’ +
 +
aPhone).value;
 +
getCell(aPhone, 2).innerHTML = document.getElementById(‘comment_’
 +
+ aPhone).value;
 +
getCell(aPhone, 3).innerHTML = ‘<a href = “#” onclick = “startEdit(\’’ +
 +
aPhone + ‘\’); return false;”>Edit</a> <a href = “#” onclick = “remove(\’’
 +
+ aPhone + ‘\’); return false;”>Remove</a>’;
 +
sendAJAXRequest(“action=edit” +
 +
“&old=” + aPhone +
 +
“&phone=” + getCell(aPhone, 0).innerHTML +
 +
“&name=” + getCell(aPhone, 1).innerHTML +
 +
“&comment=” + getCell(aPhone, 2).innerHTML);
 +
}
 +
Теперь посмотрим, как отсылаются и принимаются данные. Для
 +
этого у нас есть показательная функция loadTable(), которая загру-
 +
жает табличку с сервера. Остальные функции только отправляют
 +
данные.
 +
function loadTable() {
 +
dojo.io.queueBind({
 +
url : “/ajax?action=loadTable”,
 +
method : “get”,
 +
load : function (aType, aData, aEvent) {
 +
document.getElementById(“table”).innerHTML = aData;
 +
},
 +
preventCache : true
 +
});
 +
}
 +
Обратите внимание на две вещи. Первая – использование dojo.
 +
queueBind – это местная реализация AJAX. Нам не нужно беспокоиться,
 +
что будет использоваться «внутри» (хотя контролировать внутренности
 +
тоже можно). Вторая – параметр load. Это функция, которая вызывает-
 +
ся после загрузки данных с сервера (сами данные передаются в пара-
 +
метре aData). sendAJAXRequest выглядит абсолютно так же, только без
 +
параметра load.
 +
Кстати, в queueBind’е сами параметры обрамлены фигурными скоб-
 +
ками. Это как раз и есть объектность JavaScript. Тут создается объект с
 +
полями url, method, load и preventCache, и уже этот объект передается
 +
в качестве параметра функции.
 +
 +
==== Серверная часть ====
 +
А теперь посмотрим, как это все обрабатывать на сервере. Логика
 +
его работы осталась той же: опять действия. Только вместо выдачи
 +
большого количества HTML-кода (через шаблоны или напрямую), в
 +
большинстве случаев не выдается ничего. Вот, например, код дейс-
 +
твия удаления:
 +
String phone = aRequest.getParameter(“phone”);
 +
_recordsBook.removeRecord(phone);
 +
Только действие создания таблицы телефонов выдает код таблицы
 +
(но только ее, а не всю страничку). Остальные методы только получа-
 +
ют данные и сохраняют их. Изменение таблицы происходит на клиен-
 +
те при помощи JavaScript (как – можно посмотреть на код submitEdit
 +
выше или в полных исходных текстах на LXFDVD).
 +
 +
=== GWT ===
 +
А что делать, если вы вообще ничего не понимаете в JavaScript? Не
 +
расстраиваться, скачать GWT (он немаленький, но и перспектива
 +
читать 1000 с лишним страниц талмуда под названием «JavaScript: The
 +
Definitive Guide» тоже не вдохновляет, ведь правда? К тому же архив
 +
можно найти и на нашем DVD), установить. А дальше?
 +
Дальше процедура очень похожа на то, что позволяет делать,
 +
например, Ruby on Rails (или аналогичные каркасы – см. стр. XX):
 +
Вы создаете некий код. Клиентский, серверный, шаблон странички.
 +
При необходимости также можно применять свои CSS-стили.
 +
Затем создаете специальный XML-дескриптор. Это файл, который
 +
описывает, что нужно запускать, что будет сервером.
 +
Запускаете компилятор GWT. Он читает клиентский код, создает
 +
HTML-странички (испoльзуя ваши шаблоны) и JavaScript-код, который
 +
будет связываться с сервером и выполнять запросы, которые были
 +
описаны ранее.
 +
Запускаете сервер GWT-приложений.
 +
При этом вы не пишете ни строчки на JavaScript. HTML и CSS – да,
 +
приходится, но опять же достаточно немного. Посмотрим подробнее?
 +
 +
==== Структура приложения GWT ====
 +
Для начала стоит создать HTML-файл, который будет «заготовкой»
 +
для странички. Вот его код:
 +
<html>
 +
<head><meta name=’gwt:module’ content=’phoneBook.PhoneBook’></
 +
head>
 +
<body>
 +
<script language=”javascript” type=”text/javascript” src=”gwt.js”></
 +
script>
 +
<table>
 +
<tr><td>Phone: </td><td id=”phoneCell”></td>
 +
<td>Name: </td><td id=”nameCell”></td>
 +
<td>Comment: </td><td id=”commentCell”></td>
 +
</tr>
 +
<tr><td colspan=”6” id=”buttonCell”></td></tr>
 +
</table>
 +
<div id=”main”></div>
 +
</body>
 +
</html>
 +
Обратите внимание, что ни одной кнопки нет. И таблицы нет. Зато
 +
есть элементы, которые помечены id, в них-то и будут вставляться ком-
 +
поненты GWT, которые здесь называются виджетами.
 +
Виджеты
 +
Сами виджеты будут вставляться уже из «Java-кода». Почему в
 +
кавычках? Просто это код только пишется на Java, но потом компи-
 +
лируется в JavaScript и выполняется в браузере. Из-за этого есть мно-
 +
жество ограничений. Например, нельзя использовать конструкции
 +
Java 5, такие как новые циклы foreach, аннотации, обобщенное про-
 +
граммирование (generics) и другие. Нельзя использовать все классы
 +
подряд – только те, для которых в GWT есть «реализация» (для java.
 +
lang.* и java.util.* – есть, и это сильно упрощает дело).
 +
Код вставки виджетов выглядит примерно следующим образом:
 +
Button button = new Button(“Add new record”);
 +
RootPanel.get(“buttonCell”).add(button);
 +
Первая строчка создает кнопку, вторая вставляет ее в элемент с id
 +
«buttonCell».
 +
Список виджетов достаточно велик, и можно также создавать свои
 +
собственные. Например, для вывода таблицы контактов можно исполь-
 +
зовать FlexTable.
 +
FlexTable table = new FlexTable();
 +
Этот виджет умеет динамически изменяться, например, вот этак:
 +
aTable.setText(aRow, aColumn, “Текст в ячейку”);
 +
Этот код вставляет в ячейку таблицы текст. Можно также вставить
 +
туда и другой виджет, например, так:
 +
aTable.setWidget(aRow, aColumn, new Button(“Edit”));
 +
Для работы нужны не только виджеты, но и обработчики собы-
 +
тий. Это тоже реализовано достаточно просто: через слушателей (см.
 +
LXF92), как в Swing. Вот как, например, добавляется обработчик
 +
нажатия на кнопку:
 +
editButton.addClickListener(new ClickListener() {
 +
public void onClick(Widget aWidget) {
 +
код обработчика…
 +
}
 +
}
 +
Есть, правда, и кардинальное отличие – в асинхронности. Для этого
 +
перейдем к сервисам.
 +
 +
==== Сервисы ====
 +
Для обработки клиентских запросов пишутся так называемые серви-
 +
сы. Чтобы собрать сервис, создается два интерфейса (phoneBook.client.
 +
PhoneBookService и phoneBook.client.PhoneBookServiceAsync) и реализа-
 +
ция (phoneBook.server.PhoneBookServiceImpl). В сервис выносятся мето-
 +
ды, которые сервер выполняет по запросу клиента (у нас это добавление,
 +
удаление, редактирование записей и выдача списка записей).
 +
В «асинхронном» интерфейсе прописываются те же методы, что и
 +
в «обычном», но с одним дополнительным параметром AsyncCallback
 +
async. Это «Callback», обратный вызов. Когда делается асинхронный
 +
вызов, выполнение кода (работа с пользователем) продолжается. А
 +
когда работа асинхронного запроса завершается, вызывается этот
 +
обратный вызов, который сообщает клиенту: «Товарищ, задание пар-
 +
тии выполнено, список пользователей доставлен».
 +
Поэтому обработчики событий (например, кнопки удаления запи-
 +
си) выглядят примерно следующим образом (показан только метод
 +
onClick соответствующего обработчика):
 +
// вызов сервиса, второй параметр – это и есть обратный вызов,
 +
// то есть то, что вызывается после выполнения запроса
 +
PhoneBookService.App.getInstance().
 +
removeRecord(_phone, new AsyncCallback() {
 +
public void onFailure(Throwable aThrowable) {
 +
// ничего тут не будем делать
 +
}
 +
public void onSuccess(Object o) {
 +
// удаляем строку из таблицы, соответствующую телефону
 +
for (int i = 0; i < _table.getRowCount(); i++) {
 +
if (_table.getText(i, 0).equals(_phone)) {
 +
_table.removeRow(i);
 +
break;
 +
}
 +
}
 +
}
 +
});
 +
 +
==== Компиляция и запуск ====
 +
Ранее я говорил про дескриптор. Он очень прост и выглядит пример-
 +
но так:
 +
<module>
 +
<inherits name=’com.google.gwt.user.User’/>
 +
<entry-point class=’phoneBook.client.PhoneBook’/>
 +
<servlet path=’/PhoneBookService’ class=’phoneBook.server.
 +
PhoneBookServiceImpl’/>
 +
</module>
 +
Чтобы скомпилировать GWT-код, нужно запустить класс com.
 +
google.gwt.dev.GWTCompiler с параметром «полное имя класса, кото-
 +
рый нужно скомпилировать». Также можно указать параметр -out
 +
«каталог, куда складывать результат».
 +
Запуск специальной оболочки для отладки осуществляется при
 +
помощи класса com.google.gwt.dev.GWTShell, которому в качестве
 +
параметра передается путь к HTML-файлу запуска. Более подробные
 +
строки компиляции и запуска лучше посмотреть в примерах, которые
 +
поставляются вместе с самим GWT. А как выглядит Google Web Toolkit
 +
Development Shell, можно узнать из рисунка – «вон он, змей, в окне
 +
маячит...»
 +
Итог терзаний — все та же адресная книга и консоль
 +
на заднем плане.
 +
 +
=== Ну, а все-таки, если делать самому? ===
 +
Давайте поподробнее остановимся на некоторых подводных камнях
 +
технологии, связанных и с Java и с XMLHttpRequest’ом и с остальными
 +
ее компонентами.
 +
Во-первых, Java (точнее, серверная сторона). Тут проблема одна,
 +
UTF-8. Почему-то некоторые браузеры не понимают UTF-8 в ответах.
 +
Safari, например. Есть достаточно простой обходной путь – выставлять
 +
не совсем правильный, но работающий тип (Content-Type) содержимого ответа (response), «text/plain; charset=utf-8».
 +
Далее – клиентская часть. Тут подводных камней больше. Во-пер-
 +
вых, количество соединений: на сайт браузер разрешает обычно не
 +
более двух. Поэтому «сделаем сразу 239 AJAX-запросов» не получится.
 +
Один-два в параллели, и все.
 +
Во-вторых, выше упоминалось про callback, вызываемый, когда
 +
идет процесс загрузки ответа с сервера. Как и сколько раз он вызыва-
 +
ется – четко не определено. То есть нельзя надеяться, что он вызовется
 +
с кодом события 4 ровно один раз.
 +
В-третьих, на XMLHttpRequest не существует утвержденного стан-
 +
дарта или рекомендации, а есть только черновой вариант W3C.
 +
Поэтому реализации в разных браузерах различаются. Радует, правда,
 +
что не катастрофично.
 +
Именно поэтому примеры приводятся с использованием сторонних
 +
библиотек. А если уж и нужно сделать «самому», то стоит восполь-
 +
зоваться опытом и посмотреть их исходные тексты. В этом-то и сила
 +
Open Source.
 +
 +
=== Вот и весь AJAX ===
 +
{{Врезка
 +
|Заголовок=Ссылки и интересные статьи
 +
|Содержание=
 +
* DOJO: http://dojotoolkit.org,
 +
* DWR: http://getahead.org/dwr,
 +
* GWT: http://code.google.com/webtoolkit/,
 +
* Статья про GWT от IBM developerWorks: http://www.ibm.com/developerworks/ru/library/j-AJAX4/index.html.
 +
|Ширина=200px}}
 +
В статье рассмотрен AJAX «по верхам». Тема безграничная, так как
 +
кроме технологии, использование AJAX меняет стиль и принципы
 +
работы web-приложений. Все то, как они работали до этого, должно
 +
быть переосмыслено на совершенно другом уровне. Дерзайте, и все
 +
получится. Клиенты будут довольны, а, следовательно, разработчики
 +
будут получать больше денег.

Версия 23:47, 15 декабря 2008

Содержание

А я? (кс)

ЧАСТЬ 6 Заинтригованы ребусом в заголовке? То ли еще будет, когда прочитаете статью – Александр Бабаев большой мастер загадывать загадки, особенно если речь заходит о Web 2.0.

Когда я начинал писать эту статью в первый раз, я думал, что все достаточно просто. Сел-написал-отдал. Но сразу после этого пришлось переделывать одно приложение, которое изменило понимание и сложности, и самого Аякса. Полностью. В чем же сложность? В несоответствии. С одной стороны, Аякс позволяет приблизить логику работы интернет-приложений к обычной, «десктопной». С другой стороны – все браузеры таки разные. И это последнее «таки» местами убивает наповал. Вы думаете, что браузе- ров три? IE/Firefox/Opera? Хех. Для правильного приложения их нужно протестировать десяток. IE 5.5/6/7, Firefox 1.5/2, Opera 8/9, Safari 1.3/2/ (а теперь и 3). И у каждого свой нрав.

Ладно, что же такое Аякс?

AJAX – технология асинхронного доступа к серверу с использованием JavaScript и XML. Представим себе автомобиль. Чтобы заправить его – едем на заправку, заправляем, уезжаем. Чтобы поменять фару – едем, снимаем, ставим, уезжаем. Вот это – AJAX. А если бы Аякса не было, то для каждой такой операции нужно было бы отдавать машину в сер- вис, а из сервиса возвращалась бы другая, хоть и максимально точная копия вашей, с обновлениями (бак заправлен). Асинхронность – это вообще что-то вроде дозаправки в воздухе. То есть обновление данных без «отрыва от просмотра страницы». Давайте подробнее рассмотрим компоненты этой технологии:

A

Асинхронность реализуется разными способами. XMLHttpRequest, фреймы, Flash. Есть и другие (те же апплеты), но они распространены меньше. Второй способ – самый «простой» для человека, знакомого с HTML. Создается ма-а-аленький iframe (одна точка) и помещается куда-нибудь далеко, чтобы не было видно. Потом при необходимости подгрузить что-либо, запрос выполняется как обычно, но с целевым фреймом – тем самым, маленьким. Во фрейм загружается страничка, на которой обычно есть скрипт, который выполняется и делает свое дело (обновляет основной фрейм). Flash – это примерно то же самое, только вместо маленького скрытого фрейма используется маленькая «флэшка». Впрочем, обычно оба эти способа используются только в случае, если не подходит «основной» – XMLHttpRequest. Это название объекта в JavaScript, который может выполнять асинхронные (и синхронные) HTTP-запросы. Например: var xmlHttpRequestValue = new XMLHttpRequest(); xmlHttpRequestValue.open(“POST”, “/ajax.jsp?action=update”, true); xmlHttpRequestValue.onreadystatechange = processXmlHttpResponse; xmlHttpRequestValue.send(“Информация для пересылки серверу”); Все, запрос ушел. Сразу предупрежу, что это не рабочий код, толь- ко иллюстрация. Обратите внимание на третью строчку. Выделенное жирным – это имя функции, которая будет обрабатывать события, генерируемые во время передачи запроса, такие, как «начат прием дан- ных», «закончен прием заголовка», «закончен прием данных». Если мы хотим что-либо предпринять по окончании приема данных, эта функ- ция будет выглядеть как-то так: function processXmlHttpResponse() { if (xmlHttpRequestValue.readyState == 4) { …сделать «что-либо»… } } Число «4» как раз обозначает, что обработка завершена. Функция processXmlHttpResponse() может вызываться много раз (даже точно не определено, сколько), но как только readyState станет равным 4, мы это «поймаем» и обработаем результат.

J

JavaScript – объектный язык. То есть создать класс, как в Java, в нем нельзя. А вот новый объект – запросто. И сформировать структуру объ- екта (прописать методы, поля) «на лету» тоже можно. Чаще всего JavaScript используется как простой способ динами- чески изменять структуру документа. Например, чтобы поменять цвет параграфа, можно выполнить такой код: document.getElementById(“paragraph”).style.color = “red”; Параграф после этого покраснеет. Чтобы понимать, как добраться до отдельных параграфов, нужно изучить DOM (Document Object Model).

X

XML, который попал в AJAX последней буквой, используется (как уже упоминалось) далеко не всегда, но часто. Благо, в JavaScript есть воз- можности по достаточно простому преобразованию чего угодно в XML, да и в других языках (не только в Java) библиотеки по работе с этим форматом достаточно хорошо развиты.

Помощники

Из-за того, что реализации XMLHttpRequest различаются (в паре круп- ных деталей и, что хуже, в паре десятков мелочей), самому писать кросс-браузерную обработку достаточно тяжело и долго. Поэтому давайте посмотрим на библиотеки, в состав которых входит «AJAX- подсистема» и которые сделаны либо специально для Java-северной составляющей, либо поддерживают ее. DOJO Огромная библиотека, которая много чего умеет. Если смот- реть только на коммуникативные возможности, то тут тоже все в порядке: можно использовать XMLXttpRequest, фрейм, Flash. Первый вариант предлагается по умолчанию. Чуть ниже будет пример работы с DOJO, а сейчас просто отметим, что с учетом достаточно приличной документации, логичной организации и большого количества модулей (в том числе и для использования разных методов коммуникации), библиотеку можно назвать хорошей. DWR – Direct Web Remoting. Сделана специально для Java. Достоинство состоит в том, что DWR умеет «публиковать» классы Java в JavaScript. То есть вы пишете класс в Java, настраиваете DWR (при помощи XML-дескриптора), и после запуска сервлета (обработкой запросов от DWR занимается отдельный сервлет) в JavaScript «появля- ются» методы этого класса, которые можно вызывать, так же, как если бы это были обычные функции JavaScript. Способ удобен, но навязы- вает определенный способ коммуникации. Подходит, когда не нужно контролировать каждый байт передаваемых данных и если с JavaScript вы знакомы не очень хорошо (хотя все равно его нужно знать, так как кроме коммуникации есть много чего еще). GWT – Google Web Toolkit. В свое время эта библиотека наделала много шуму. Все дело в принципе ее работы. В общем и целом, если на DWR мы пишем на Java коммуникацию, то на GWT – все приложение. Затем GWT компилирует его в клиентскую часть (HTML + JavaScript) и серверную часть (Java). То есть клиент получается автоматически (не целиком, но близко к тому). GWT берет на себя все заботы и по вза- имодействию клиента с сервером, и по созданию интерфейса (этот процесс несколько напоминает Swing), и многое другое. В принципе, достаточно хорошее решение, естественно со своими нюансами. Мы еще посмотрим на него поближе... к концу статьи.

Адресная книга с AJAX

Настало время потренироваться. Давайте возьмем нашу адресную книгу и сделаем так, чтобы показывалась страничка с табличкой (спис- ком телефонов), а по нажатии на ссылки вместо перехода на новые странички менялся сам список: удалялись и добавлялись строки и так далее. Использовать будем DOJO. Идеология от этого не пострадает, но станет немного проще, короче и понятнее.

HTML/JavaScript

Вот строка HTML-таблицы, из которой видно, как будет выводиться список телефонов. <tr id=”1232323” > <td>1232323</td><td>Vasya Beanov</td><td><td>…</td><td><a href=”#” on click=”remove(‘1232323’); return false;”>Remove</a> / <a href=”#” on click=”startEdit(‘1232323’); return false;”>Edit</a></td> </tr> Обратите внимание на id=”…”. Это своеобразные метки, которые позволят при необходимости найти нужную строку. Теперь рассмот- рим JavaScript-код, который выполняется при нажатии на «кнопку» Edit (как самую сложную). function startEdit(aPhone) { getCell(aPhone, 0).innerHTML = ‘<input type=”text” value =”’ + getCell(aPhone, 0).innerHTML + ‘” id =”phone_’ + aPhone + ‘”/>’; getCell(aPhone, 1).innerHTML = ‘<input type=”text” value = “’ + getCell(aPhone, 1).innerHTML + ‘” id = “name_’ + aPhone + ‘”/>’; getCell(aPhone, 2).innerHTML = ‘<input type = “text” value =”’ + getCell(aPhone, 2).innerHTML + ‘” id =”comment_’ + aPhone + ‘”/>’; getCell(aPhone, 3).innerHTML = ‘<a href = “#” onclick = “submitEdit(\’’ + aPhone + ‘\’); return false;”>Save changes</a>’; } Простым русским языком этом можно выразить так: ячейки с текс- том заменяются на ячейки с полями ввода. После редактирования и нажатия на кнопку Save changes данные отсылаются на сервер, и в ячейках снова прописывается текст. function submitEdit(aPhone) { getCell(aPhone, 0).innerHTML = document.getElementById(‘phone_’ + aPhone).value; getCell(aPhone, 1).innerHTML = document.getElementById(‘name_’ + aPhone).value; getCell(aPhone, 2).innerHTML = document.getElementById(‘comment_’ + aPhone).value; getCell(aPhone, 3).innerHTML = ‘<a href = “#” onclick = “startEdit(\’’ + aPhone + ‘\’); return false;”>Edit</a> <a href = “#” onclick = “remove(\’’ + aPhone + ‘\’); return false;”>Remove</a>’; sendAJAXRequest(“action=edit” + “&old=” + aPhone + “&phone=” + getCell(aPhone, 0).innerHTML + “&name=” + getCell(aPhone, 1).innerHTML + “&comment=” + getCell(aPhone, 2).innerHTML); } Теперь посмотрим, как отсылаются и принимаются данные. Для этого у нас есть показательная функция loadTable(), которая загру- жает табличку с сервера. Остальные функции только отправляют данные. function loadTable() { dojo.io.queueBind({ url : “/ajax?action=loadTable”, method : “get”, load : function (aType, aData, aEvent) { document.getElementById(“table”).innerHTML = aData; }, preventCache : true }); } Обратите внимание на две вещи. Первая – использование dojo. queueBind – это местная реализация AJAX. Нам не нужно беспокоиться, что будет использоваться «внутри» (хотя контролировать внутренности тоже можно). Вторая – параметр load. Это функция, которая вызывает- ся после загрузки данных с сервера (сами данные передаются в пара- метре aData). sendAJAXRequest выглядит абсолютно так же, только без параметра load. Кстати, в queueBind’е сами параметры обрамлены фигурными скоб- ками. Это как раз и есть объектность JavaScript. Тут создается объект с полями url, method, load и preventCache, и уже этот объект передается в качестве параметра функции.

Серверная часть

А теперь посмотрим, как это все обрабатывать на сервере. Логика его работы осталась той же: опять действия. Только вместо выдачи большого количества HTML-кода (через шаблоны или напрямую), в большинстве случаев не выдается ничего. Вот, например, код дейс- твия удаления: String phone = aRequest.getParameter(“phone”); _recordsBook.removeRecord(phone); Только действие создания таблицы телефонов выдает код таблицы (но только ее, а не всю страничку). Остальные методы только получа- ют данные и сохраняют их. Изменение таблицы происходит на клиен- те при помощи JavaScript (как – можно посмотреть на код submitEdit выше или в полных исходных текстах на LXFDVD).

GWT

А что делать, если вы вообще ничего не понимаете в JavaScript? Не расстраиваться, скачать GWT (он немаленький, но и перспектива читать 1000 с лишним страниц талмуда под названием «JavaScript: The Definitive Guide» тоже не вдохновляет, ведь правда? К тому же архив можно найти и на нашем DVD), установить. А дальше? Дальше процедура очень похожа на то, что позволяет делать, например, Ruby on Rails (или аналогичные каркасы – см. стр. XX): Вы создаете некий код. Клиентский, серверный, шаблон странички. При необходимости также можно применять свои CSS-стили. Затем создаете специальный XML-дескриптор. Это файл, который описывает, что нужно запускать, что будет сервером. Запускаете компилятор GWT. Он читает клиентский код, создает HTML-странички (испoльзуя ваши шаблоны) и JavaScript-код, который будет связываться с сервером и выполнять запросы, которые были описаны ранее. Запускаете сервер GWT-приложений. При этом вы не пишете ни строчки на JavaScript. HTML и CSS – да, приходится, но опять же достаточно немного. Посмотрим подробнее?

Структура приложения GWT

Для начала стоит создать HTML-файл, который будет «заготовкой» для странички. Вот его код: <html> <head><meta name=’gwt:module’ content=’phoneBook.PhoneBook’></ head> <body> <script language=”javascript” type=”text/javascript” src=”gwt.js”></ script>

Phone: Name: Comment:

</body> </html> Обратите внимание, что ни одной кнопки нет. И таблицы нет. Зато есть элементы, которые помечены id, в них-то и будут вставляться ком- поненты GWT, которые здесь называются виджетами. Виджеты Сами виджеты будут вставляться уже из «Java-кода». Почему в кавычках? Просто это код только пишется на Java, но потом компи- лируется в JavaScript и выполняется в браузере. Из-за этого есть мно- жество ограничений. Например, нельзя использовать конструкции Java 5, такие как новые циклы foreach, аннотации, обобщенное про- граммирование (generics) и другие. Нельзя использовать все классы подряд – только те, для которых в GWT есть «реализация» (для java. lang.* и java.util.* – есть, и это сильно упрощает дело). Код вставки виджетов выглядит примерно следующим образом: Button button = new Button(“Add new record”); RootPanel.get(“buttonCell”).add(button); Первая строчка создает кнопку, вторая вставляет ее в элемент с id «buttonCell». Список виджетов достаточно велик, и можно также создавать свои собственные. Например, для вывода таблицы контактов можно исполь- зовать FlexTable. FlexTable table = new FlexTable(); Этот виджет умеет динамически изменяться, например, вот этак: aTable.setText(aRow, aColumn, “Текст в ячейку”); Этот код вставляет в ячейку таблицы текст. Можно также вставить туда и другой виджет, например, так: aTable.setWidget(aRow, aColumn, new Button(“Edit”)); Для работы нужны не только виджеты, но и обработчики собы- тий. Это тоже реализовано достаточно просто: через слушателей (см. LXF92), как в Swing. Вот как, например, добавляется обработчик нажатия на кнопку: editButton.addClickListener(new ClickListener() { public void onClick(Widget aWidget) { код обработчика… } } Есть, правда, и кардинальное отличие – в асинхронности. Для этого перейдем к сервисам.

Сервисы

Для обработки клиентских запросов пишутся так называемые серви- сы. Чтобы собрать сервис, создается два интерфейса (phoneBook.client. PhoneBookService и phoneBook.client.PhoneBookServiceAsync) и реализа- ция (phoneBook.server.PhoneBookServiceImpl). В сервис выносятся мето- ды, которые сервер выполняет по запросу клиента (у нас это добавление, удаление, редактирование записей и выдача списка записей). В «асинхронном» интерфейсе прописываются те же методы, что и в «обычном», но с одним дополнительным параметром AsyncCallback async. Это «Callback», обратный вызов. Когда делается асинхронный вызов, выполнение кода (работа с пользователем) продолжается. А когда работа асинхронного запроса завершается, вызывается этот обратный вызов, который сообщает клиенту: «Товарищ, задание пар- тии выполнено, список пользователей доставлен». Поэтому обработчики событий (например, кнопки удаления запи- си) выглядят примерно следующим образом (показан только метод onClick соответствующего обработчика): // вызов сервиса, второй параметр – это и есть обратный вызов, // то есть то, что вызывается после выполнения запроса PhoneBookService.App.getInstance(). removeRecord(_phone, new AsyncCallback() { public void onFailure(Throwable aThrowable) { // ничего тут не будем делать } public void onSuccess(Object o) { // удаляем строку из таблицы, соответствующую телефону for (int i = 0; i < _table.getRowCount(); i++) { if (_table.getText(i, 0).equals(_phone)) { _table.removeRow(i); break; } } } });

Компиляция и запуск

Ранее я говорил про дескриптор. Он очень прост и выглядит пример- но так: <module> <inherits name=’com.google.gwt.user.User’/> <entry-point class=’phoneBook.client.PhoneBook’/> <servlet path=’/PhoneBookService’ class=’phoneBook.server. PhoneBookServiceImpl’/> </module> Чтобы скомпилировать GWT-код, нужно запустить класс com. google.gwt.dev.GWTCompiler с параметром «полное имя класса, кото- рый нужно скомпилировать». Также можно указать параметр -out «каталог, куда складывать результат». Запуск специальной оболочки для отладки осуществляется при помощи класса com.google.gwt.dev.GWTShell, которому в качестве параметра передается путь к HTML-файлу запуска. Более подробные строки компиляции и запуска лучше посмотреть в примерах, которые поставляются вместе с самим GWT. А как выглядит Google Web Toolkit Development Shell, можно узнать из рисунка – «вон он, змей, в окне маячит...» Итог терзаний — все та же адресная книга и консоль на заднем плане.

Ну, а все-таки, если делать самому?

Давайте поподробнее остановимся на некоторых подводных камнях технологии, связанных и с Java и с XMLHttpRequest’ом и с остальными ее компонентами. Во-первых, Java (точнее, серверная сторона). Тут проблема одна, UTF-8. Почему-то некоторые браузеры не понимают UTF-8 в ответах. Safari, например. Есть достаточно простой обходной путь – выставлять не совсем правильный, но работающий тип (Content-Type) содержимого ответа (response), «text/plain; charset=utf-8». Далее – клиентская часть. Тут подводных камней больше. Во-пер- вых, количество соединений: на сайт браузер разрешает обычно не более двух. Поэтому «сделаем сразу 239 AJAX-запросов» не получится. Один-два в параллели, и все. Во-вторых, выше упоминалось про callback, вызываемый, когда идет процесс загрузки ответа с сервера. Как и сколько раз он вызыва- ется – четко не определено. То есть нельзя надеяться, что он вызовется с кодом события 4 ровно один раз. В-третьих, на XMLHttpRequest не существует утвержденного стан- дарта или рекомендации, а есть только черновой вариант W3C. Поэтому реализации в разных браузерах различаются. Радует, правда, что не катастрофично. Именно поэтому примеры приводятся с использованием сторонних библиотек. А если уж и нужно сделать «самому», то стоит восполь- зоваться опытом и посмотреть их исходные тексты. В этом-то и сила Open Source.

Вот и весь AJAX

В статье рассмотрен AJAX «по верхам». Тема безграничная, так как кроме технологии, использование AJAX меняет стиль и принципы работы web-приложений. Все то, как они работали до этого, должно быть переосмыслено на совершенно другом уровне. Дерзайте, и все получится. Клиенты будут довольны, а, следовательно, разработчики будут получать больше денег.

Персональные инструменты
купить
подписаться
Яндекс.Метрика