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

LXF95:Java EE

Материал из Linuxformat
Перейти к: навигация, поиск

Содержание

А я? (кс)

ЧАСТЬ 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-приложений. Все то, как они работали до этого, должно быть переосмыслено на совершенно другом уровне. Дерзайте, и все получится. Клиенты будут довольны, а, следовательно, разработчики будут получать больше денег.

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