Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
107
Добавлен:
02.05.2014
Размер:
6.34 Mб
Скачать

226 Часть II. Основные подходы к разработке приложений

planets.commands.UpdatePropertyCommand.parseResponse=

function(docEl)

{

var attrs=docEl.attributes;

var status=attrs.getNamedItem("status").value; if (status!="ok")

{

var

reason=attrs.getNamedItem("message").value;

a l e r t ( " f a i l e d

to update "

+ t h i s . f i e l d + "

to "+this . value

+ "\n\n"+reason);

}

}

Данный объект предоставляет уникальный идентификатор команды и инкапсулирует параметры, необходимые серверу. Функция toRequeststring() оформляет сама себя в виде фрагмента XML-кода, используя специальную функцию, которую мы присоединили к прототипу Object.

Object.prototype.simpleXmlify=function(tagname){ var xml="<"+tagname;

for

(i in

this){

if

(!this[i] instanceof Function)

{

xml+="

"+i+"=\""+this[i]+"\"";

}

 

 

}

xml+="/>";- return xml;

)

В результате создается простой XML-дескриптор (для удобства восприятия он отформатирован вручную).

<command type='update Property' id='001_diameter' planetld='mercury' field='diameter'

value='3'

/>

Заметьте, что уникальный идентификатор формируется из идентификатора планеты и имени свойства. Мы не можем передать серверу несколько вариантов одного и того же значения. Если перед передачей содержимого очереди мы несколько раз отредактируем свойство, каждое последующее значение будет заменять предыдущее.

В составе запроса POST, передаваемого серверу, может содержаться один или несколько описанных выше дескрипторов, в зависимости от частоты опроса и активности пользователя. Сервер обрабатывает каждую команду и сохраняет результаты, формируя ответ. Обработчик onload, принадлежащий CommandQueue, распознает дескрипторы в ответе, сравнивает их с объектами Command в массиве sent и вызывает метод parseResponse () объекта Command. Ответ может выглядеть следующим образом:

Глава 5. Роль сервера в работе Ajax-приложения 227

<commands>

<command id='001_diameter' status='ok'/> <command id='003_albedo'

s t a t u s = ' f a i l e d '

message='value out of range'/>

<command id='004_hairColor'

s t a t u s = ' f a i l e d '

message='invalid property name'/>

< / cornmands >

Как видно из сообщения, информация о диаметре Меркурия была обновлена, но другие два обновления отклонены. Причины описаны посредством атрибута message. Пользователь оповещается о возникающих проблемах (обычно для этого используется функция a l e r t ()) и может предпринять ответные действия.

Компонент, обрабатывающий запросы на стороне сервера, должен выделять из данных запроса отдельные команды и передавать каждую команду соответствующему объекту обработчика. По мере обработки команд результаты записываются в HTTP-ответ. Код простого Java-сервлета, предназначенного для решения описанной задачи, представлен в листинге 5.15.

Листинг 5.15. Содержимое файла CommandServlet .Java public class CommandServlet extends HttpServlet

{

private Map commandTypes=null;

public void init() throws ServletException

{

ServletConfig config=getServletConfig(); // О Конфигурация обработчиков commandTypes=new HashMap();

boolean more=true;

f o r ( i n t counter=l;more;counter++){

String typeName=config.getlnitParameter("type"+counter); String typelmpl=config.getlnitParameter("impl"+counter);

if (typeName==null || typelmpl==null){ more=false;

}

else

{

try{

Class cls=Class.forName(typelmpl); commandTypes.put(typeName,els);

}

catch (ClassNotFoundException clanfex)

{

t h i s . l o g (

"couldn'tresolve handler class name"+typelmpl);

}

}

}

}

228 Часть II. Основные подходы кразработкеприложений

protected void doPost

(

HttpServletRequest req, HttpServletResponse resp

)

throws IOException{

// 0 Обработать запрос resp.setContentType("text/xml"); Reader reader=req.getReader(); Writer writer=resp.getWriter() ;

try{ SAXBuilder builder=new SAXBuilder(false);

// © Обработать XML-данные Document doc=builder.build(reader); Element root=doc.getRootElement();

if ("commands".equals(root.getName())){

for(Iterator iter=root.getChildren("command").iterator(); iter.hasNext();)

{

Element el=(Element)(iter.next());

String type=el.getAttributeValue("type"); XMLCommandProcessor command=getCommand(type,writer); if (command!=null)

{

// О Делегировать обработку

Element result=command.processXML(el); writer.write(result.toString());

}

}

}

else

{

sendError(writer,

"incorrect document format - " +"expected top-level command tag");

}

}

catch (JDOMException jdomex){

sendError(writer,"unable to parse request document");

}

}

private XMLCommandProcessor getCommand (String type,Writer writer)

throws IOException{

// © Соответствие обработчика команде XMLCommandProcessor cmd=null;

Class cls=(Class)(commandTypes.get(type)); if (cls!=null){

Глава 5. Роль сервера вработеAjax-приложения

229

try{ cmd=(XMLCommandProcessor)(els.newlnstance()) ; }catch (ClassCastException castex){

sendError(writer,

"class "+cls.getName() +" is not a command");

}

catch (InstantiationException instex)

{

sendError(writer,

"not able to create class "+cls.getName()); } catch (IllegalAccessException illex)

{

sendError(writer,

"not allowed to create class "+cls.getName());

}

}else{

sendError(writer,"no command type registered for "+type);

}

 

 

 

return

cmd;

}

 

 

 

p r i v a t e v o i d s e n d E r r o r

 

(Writer

w r i t e r , S t r i n g message) throws IOException{

 

w r i t e r . w r i t e ( " < e r r o r msg='"+message+" ' / > " ) ;

 

w r i t e r . f l u s h ( ) ;

}

 

 

_ }

 

 

 

 

Данный сервлет поддерживает карту объектов XMLCommandProcessor, для конфигурирования которой используется интерфейс ServletConfig О. При использовании более мощных базовых средств можно воспользоваться для этой цели конфигурационным XML-файлом. При обработке запроса POST © для разбора XML-данных используется JDOM ©, а затем осуществляется перебор дескрипторов <coramand>, для которых атрибуты соответствуют объекту обработчика XMLCommandProcessor О. В карте содержатся определения классов, на основе которых в методе getCommand () мы создаем конкретные экземпляры, используя механизм отражения ©.

В интерфейсе XMLCommandProcessor объявлен единственный метод.

public interface XMLCommandProcessor { Element processXML(Element el); }

В данном интерфейсе предполагается, что для представления XMLДанных будут применяться библиотеки JDOM. Объектами Element являются как параметры, так и возвращаемое значение. Простой класс, реализующий Данный интерфейс и предназначенный для обновления данных о планетах, показан в листинге 5.16.

230 Часть II. Основные подходы кразработке приложений

Листинг 5.16. Содержимое файла PlanetUpdateCoimandProcessor. Java public class PlanetUpdateCommandProcessor

implements XMLCommandProcessor

{

public Element processXML(Element el)

{

//О Создать XML-узел результатов Element result=new Element("command"); String id-el.getAttributeValue("id"); result.setAttribute("id", id);

String status=null; String reason=null;

String planetld=el.getAttributeValue("planetld"); String field=el.getAttributeValue("field"); String value=el.getAttributeValue("value");

//@ Обращение к модели предметной области Planet planet=findPlanet(planetld);

if (planet==null){ status-"failed";

reason="no planet found for id "+planetld; }else{

Double numValue=new Double(value); Object[] args=new Object[]{ numValue

};

String method = "set"+field.substring(O,1).toUpperCase() +field.substring(1);

Statement statement=new Statement(planet,method,args); try {

// © Обновить модель предметной области statement.execute();

status="ok";

} catch (Exception e) { status="failed";

reason="unable to set value "+value+" for field "+field;

}

}

result.setAttribute("status",status); if (reason!=null){

result.setAttribute("reason",reason);

}

return result;

}

private Planet findPlanet(String planetld)

{

// О Использовать ORM для модели предметной области return null;

}

}

Глава 5. Роль сервера в работе Ajax-приложения 231

JDOM используется не только для разбора XML-данных, содержащихся в запросе, но и для генерации ответа. Корневой узел О и дочерние узлы создаются в методе processXML(). Для доступа к модели предметной области на стороне сервера используется метод f indPlanet () ©; ему передается идентификатор объекта. Реализация метода f indPlanet () здесь не рассматривается; заметим лишь, что в нем реализуется обмен с базой данных традиционными способами О. Для обновления модели предметной области © применяется механизм отражения.

Так в общих чертах выглядит архитектура, основанная на использовании очереди и используемая для объединения частных действий по обновлению модели предметной области в одну HTTP-транзакцию. Она позволяет не только синхронизировать модели на стороне клиента и на стороне сервера, но и эффективно управлять трафиком. Как было сказано в разделе 5.3, такое решение может быть использовано совместно с JSF и другими подобными средствами, когда структура пользовательского интерфейса и модель взаимодействия существенно зависят от сервера. В данном случае мы лишь реализовали эффективный способ обновления модели предметной области на разных уровнях.

На этом мы заканчиваем разговор о средствах обеспечения взаимодействия клиента и сервера и обзор основных элементов, определяющих структуру Aj ах-приложений. По сути, мы заложили основы языка запросов к серверу в рамках Ajax-приложения и получили представление о возможностях, доступных для его реализации.

5.6. Резюме

Мы начали данную главу с рассмотрения основных функций сервера в составе Ajax-приложения, а именно с доставки клиентского кода браузеру и предоставления клиенту данных в процессе работы. Рассматривая эти вопросы, мы учли наиболее популярные языки и инструменты, используемые для создания серверных программ. Они ориентированы на классические Web-приложения, и мы выяснили, насколько они подходят для Ajax. Инструменты, предназначенные для создания серверных программ, очень быстро изменяются, поэтому, вместо того, чтобы обсуждать конкретные продукты, мы разделили их на категории в зависимости от используемой архитектуры. В результате было выделено три основных направления: архитектура Model2, средства на основе компонентов и архитектура, ориентированная на использование служб. На сегодняшний день есть основания считать, что лучше всего для Ajax подходит SOA, однако попытки адаптировать другие архитектуры также могут Увенчаться успехом.

232 Часть II. Основные подходы к разработке приложений

Далее мы выделили три основных подхода к решению задачи доставки данных от сервера клиенту: ориентированный на содержимое, ориентированный на сценарий и ориентированный на данные. Если классическое Webприложение больше всего соответствует архитектуре, ориентированной на содержимое, то приложение Ajax больше укладывается в схему, ориентированную на данные. Взаимодействие, ориентированное на сценарии, занимает промежуточную позицию. Обсуждая архитектуру, ориентированную на данные, мы выяснили, что XML — не единственно возможный формат представления данных. В некоторых случаях при доставке информации клиенту может успешно применяться формат JSON.

И наконец, мы описали способы передачи данных на сервер, используя для этой цели HTML-формы и объект XMLHttpRequest. Мы также обсуждали возможности управления передачей данных путем создания на стороне клиента очереди объектов Command. Данный подход обеспечивает существенное повышение производительности, снижая как нагрузку на сервер, так и сетевой трафик. Переходя к взаимодействию, ориентированному на данные, мы отказываемся от действий, основанных на удаленном вызове процедур, и переходим к работе с документом.

Этой главой мы завершаем разговор о базовых технологиях Ajax. Мы обсудили все основные вопросы и даже затронули более сложные темы. В последующих трех главах мы вернемся к проблеме применимости программ и выясним, чем же отличаются поделки, пусть даже талантливые, от настоящих продуктов, в которых нуждаются пользователи.

5.7. Ресурсы

В данной главе были упомянуты некоторые инструменты разработки. Ниже приведены их URL.

Struts (http://struts.apache.org).

Tapestry (http://jakarta.apache.org/tapestry/).

JSF (http://java.sun.com/j2ee/javaserverfaces/faq.html). PHP-MVC (http://www.phpmvc.net).

По данным исследований (http://wicket.sourceforge.net/Introduc tion.html), в настоящее время только для Java существует более 60 наборов базовых средств.

JSF — это категория инструментов, в которую входят различные продукты. Кито Манн (Kito Mann), автор книги JavaServer Faces in Action (Manning, 2004), поддерживает Web-портал, посвященный JSF (http://www.jsfcentral.com/). Материалы, подготовленные Грегом Мюрреем (Greg Murray) и его коллегами в ходе обсуждения тем Ajax и JSF, доступны по адресу https://bpcatalog.dev.java.net/nonav/ajax/jsf-ajax/ frames.html. AjaxFaces представляет собой реализацию JSF для Ajax (http://www.ajaxfaces.com); этот продукт распространяется на коммерческой основе. Готовится модификация Apache Open Source MyFaces (http://myfaces.apache.org/sandbox/inputSuggestAjax.html) в соответствии с требованиями Ajax.

Глава 5. Роль сервера в работе Ajax-приложения 233

На момент написания данной книги Microsoft Atlas находился в процессе пазработки, но первые реализации скоро должны быть доступны. Менеджер проекта Atlas, Скотт Гатри (Scott Guthrie), публикует материалы, имеющие отношение к Ajax (http://weblogs.asp.net/scottgu/archive/2005/06/28/ 416185.aspx).

Библиотеки JSON-RPC, ориентированные на различные языки программирования, находятся по адресу http://www.json-rpc.org/impl.xhtml.

Часть III

Создание

профессиональных Ajax-приАожений

Теперь ваше Ajах-приложение может взаимодействовать с сервером, получать от него информацию и обновлять хранящиеся на нем данные. Однако, если вы хотите, чтобы ваш продукт был по достоинству оценен пользователями, вам рано останавливаться. В этой части вы узнаете, как добиться того, чтобы приложение было простым в использовании, защищенным и работало достаточно быстро.

Инфор,

по,

Вэтой главе...

Основные характеристики практичного кода

Принципы работы системы оповещения

Набор базовых средств для оповещения

Выделение измененных данных