- •Введение
- •Несколько слов о книге
- •Глава 1. Каким должен бытъ Web-интерфейс
- •Действия пользователя при работе с приложением
- •Накладные расходы при работе в сети
- •Асинхронное взаимодействие
- •Независимый и переходный образы использования
- •Четыре основных принципа Ajax
- •Браузер имеет дело с приложением, а не с содержимым
- •Сервер доставляет данные, а не содержимое
- •Реальное кодирование требует порядка
- •Применение богатых клиентов Ajax
- •Системы, созданные с использованием Ajax
- •Google Maps
- •Альтернативные технологии
- •Macromedia Flash
- •Java Web Start
- •Резюме
- •Ресурсы
- •Основные элементы Ajax
- •JavaScript изучался не зря
- •Определение внешнего вида с помощью CSS
- •Селекторы CSS
- •Свойства стилей
- •Простой пример использования CSS
- •Обработка DOM с помощью JavaScript
- •Поиск узла DOM
- •Создание узла DOM
- •Добавление стилей к документу
- •Свойство innerHTML
- •Асинхронная загрузка с использованием XML
- •Элементы IFrame
- •Объекты XmlDocument и XMLHttpRequest
- •Использование фуниции обратного вызова для контроля запроса
- •Жизненный цикл процедуры поддержки запроса
- •Отличия Ajax от классических технологий
- •Резюме
- •Ресурсы
- •Порядок из хаоса
- •Образы разработки
- •Реструктуризация и Ajax
- •Во всем надо знать меру
- •Реструктуризация в действии
- •Варианты применения реструктуризации
- •Несоответствие браузеров: образы разработки Fagade и Adapter
- •Управление обработчиками событий: образ разработки Observer
- •Повторное использование обработчиков событий: образ разработки Command
- •Обеспечение единственной ссылки на ресурс: образ разработки Singleton
- •"Модель-представление-контроллер "
- •Серверная программа Ajax, созданная без применения образов разработки
- •Реструктуризация модели
- •Разделение содержимого и представления
- •Библиотеки независимых производителей
- •Библиотеки, обеспечивающие работу с различными браузерами
- •Компоненты и наборы компонентов
- •Элементы, располагаемые на стороне сервера
- •Резюме
- •Ресурсы
- •Применение архитектуры MVC к программам различных уровней
- •Применение архитектуры MVC к объектам, присутствующим в среде браузера
- •Представление в составе Ajax-приложения
- •Отделение логики от представления
- •Отделение представления от логики
- •Контроллер в составе Ajax-приложения
- •Классические JavaScript-обработчики
- •Модель обработки событий W3C
- •Реализация гибкой модели событий в JavaScript
- •Модель в составе Ajax-приложения
- •Использование JavaScript для моделирования предметной области
- •Взаимодействие с сервером
- •Генерация представления на основе модели
- •Отражение объектов JavaScript
- •Обработка массивов и объектов
- •Включение контроллера
- •Резюме
- •Ресурсы
- •Программы, выполняемые на сервере
- •Создание программ на стороне сервера
- •N-уровневые архитектуры
- •Управление моделью предметной области на стороне клиента и на стороне сервера
- •Принципы создания программ на сервере
- •Серверные программы, не соответствующие основным принципам разработки
- •Использование архитектуры Model!
- •Использование архитектуры на базе компонентов
- •Архитектуры, ориентированные на использование Web-служб
- •Частные решения: обмен данными
- •Взаимодействие, затрагивающее только клиентскую программу
- •Пример отображения информации о планетах
- •Взаимодействие, ориентированное на содержимое
- •Взаимодействие, ориентированное на сценарий
- •Передача данных серверу
- •Использование HTML-форм
- •Использование объекта XMLHttpRequest
- •Управление обновлением модели
- •Резюме
- •Ресурсы
- •Создание качественного приложения
- •Отклик программы
- •Надежность
- •Согласованность
- •Простота
- •Как получить результат
- •Предоставление сведений пользователю
- •Поддержка ответов на собственные запросы
- •Обработка обновлений, выполненных другими пользователями
- •Создание системы оповещения
- •Основные принципы оповещения
- •Реализация базовых средств оповещения
- •Отображение пиктограмм в строке состояния
- •Отображение подробных сообщений
- •Формирование готовой системы
- •Предоставление информации в запросах
- •Информация о новизне данных
- •Простой способ выделения данных
- •Выделение данных с использованием библиотеки Scriptaculous
- •Резюме
- •Ресурсы
- •JavaScript и защита браузера
- •Политика "сервера-источника"
- •Особенности выполнения сценариев в Ajax-приложении
- •Проблемы с поддоменами
- •Взаимодействие с удаленным сервером
- •Взаимодействие с Web-службами
- •Защита конфиденциальной информации
- •Вмешательство в процесс передачи данных
- •Организация защищенного НТТР-взаимодействия
- •Передача шифрованных данных в ходе обычного HTTP-взаимодействия
- •Управление доступом к потокам данных Ajax
- •Создание защищенных программ на уровне сервера
- •Ограничение доступа к данным из Web
- •Резюме
- •Ресурсы
- •Что такое производительность
- •Скорость выполнения JavaScript-программ
- •Определение времени выполнения приложения
- •Использование профилировщика Venkman
- •Оптимизация скорости выполнения Ajax-приложения
- •Использование памяти JavaScript-кодом
- •Борьба с утечкой памяти
- •Особенности управления памятью в приложениях Ajax
- •Разработка с учетом производительности
- •Простой пример управления памятью
- •Как уменьшить объем используемой памяти в 150 раз
- •Резюме
- •Ресурсы
- •Сценарий двойной комбинации
- •Недостатки клиентского решения
- •Недостатки клиентского решения
- •Архитектура клиента
- •Разработка взаимодействия клиент/сервер
- •Реализация сервера: VB.NET
- •Написание кода сервера
- •Представление результатов
- •Применение каскадных таблиц стилей
- •Дополнительные вопросы
- •Запросы при выборе нескольких элементов
- •Переход от двойного связного выбора к тройному
- •Реструктуризация
- •Новый и улучшенный объект netContentLoader
- •Создание компонента двойного списка
- •Резюме
- •Глава 10. Опережающий ввод
- •Изучаем опережающий ввод
- •Типичные элементы приложений опережающего ввода
- •Google Suggest
- •Ajax как средство опережающего ввода
- •Структура серверной части сценария: С#
- •Сервер и база данных
- •Тестирование серверного кода
- •Структура клиентской части сценария
- •HTML
- •JavaScript
- •Обращение к серверу
- •Дополнительные возможности
- •Реструктуризация
- •День 1: план разработки компонента TextSuggest
- •День 3: включаем Ajax
- •День 4: обработка событий
- •День 5: пользовательский интерфейс всплывающего окна с предлагаемыми вариантами
- •Итоги
- •Резюме
- •Эволюционирующий портал
- •Классический портал
- •Портал с богатым пользовательским интерфейсом
- •Создание портала с использованием Java
- •Таблица пользователя
- •Серверная часть кода регистрации: Java
- •Структура регистрации (клиентская часть)
- •Реализация окон DHTML
- •База данных окон портала
- •Серверный код окна портала
- •Добавление внешней библиотеки JavaScript
- •Возможность автоматического сохранения
- •Адаптация библиотеки
- •Автоматическая запись информации в базе данных
- •Реструктуризация
- •Определение конструктора
- •Адаптация библиотеки AjaxWindows.js
- •Задание команд портала
- •Выводы
- •Резюме
- •Понимание технологий поиска
- •Классический поиск
- •"Живой" поиск с использованием Ajax и XSLT
- •Возврат результатов клиенту
- •Код клиентской части сценария
- •Настройка клиента
- •Инициализация процесса
- •Код серверной части приложения: РНР
- •Создание XML-документа
- •Создание документа XSLT
- •Объединение документов XSL и XML
- •Совместимость с браузером Microsoft Internet Explorer
- •Совместимость с браузерами Mozilla
- •Последние штрихи
- •Применение каскадных таблиц стилей
- •Улучшение поиска
- •Поддержка браузерами Opera и Safari
- •Использовать ли XSLT
- •Решение проблемы закладок
- •Реструктуризация
- •Объект XSLTHelper
- •Компонент "живого" поиска
- •Выводы
- •Резюме
- •Считывание информации из внешнего мира
- •Поиск XML-лент
- •Изучение структуры RSS
- •Богатый пользовательский интерфейс
- •Чтение лент
- •HTML-структура без таблиц
- •Гибкое CSS-форматироеание
- •Глобальный уровень
- •Предварительная загрузка средствами Ajax
- •Богатый эффект перехода
- •Правила прозрачности, учитывающие индивидуальность браузеров
- •Реализация затухающего перехода
- •Интеграция таймеров JavaScript
- •Дополнительные возможности
- •Введение дополнительных лент
- •Интеграция функций пропуска и паузы
- •Как избежать ограничений проекта
- •Обход системы безопасности браузеров Mozilla
- •Изменение масштаба приложения
- •Реструктуризация
- •Модель приложения
- •Представление приложения
- •Контроллер приложения
- •Выводы
- •Резюме
- •Отладчики
- •Для чего нужен отладчик
- •Средство Safari DOM Inspector для Mac OS X
- •Ресурсы
- •JavaScript — это не Java
- •Формирование объектов
Глава 5. Роль сервера в работе Ajax-приложения |
221 |
Конструктору объекта мы передаем несколько новых параметров |
О. |
Из них обязательными являются URL (в соответствии с атрибутом action формы) и обработчик onload. Можно также задать HTTP-метод, параметры и тип запроса. Заметьте, что при передаче пар ключ-значение посредством метода POST надо указать тип содержимого application/x-www-f ormurlencoded. Если при вызове функции тип явно не задан, мы устанавливаем это значение автоматически. HTTP-метод используется при вызове метода open () объекта XMLHttpRequest а параметры — при обращении к методу send( ). Таким образом, вызов конструктора имеет следующий вид:
var loader=net.ContentLoader( 'myFormHandlerURL.php', showResponse, null, ' POST', 'username=dave&password=letmein'
); В результате выполнения этого выражения будет сформирован такой же
запрос, как и при использовании метода submitData(), представленного в листинге 5.11. Заметьте, что параметры передаются в виде строки, которая кодируется так же, как и при передаче данных формы, например:
name=daveSjob=book&work=Ajax_In+Action
Так действуют основные механизмы передачи данных серверу. Они могут активизироваться в результате различных событий: ввода пользователем текста, перемещения мыши, перетаскивания объекта и т.д. В следующем разделе мы вернемся к объекту Objectviewer, рассмотренному в главе 4, и выясним, как можно управлять обновлением модели предметной области.
5.5.3. Управление обновлением модели
В главе 3 мы рассмотрели универсальный объект Objectviewer, предназначенный для представления сложных моделей, и обсудили простой пример,
вкотором данный объект используется для просмотра информации о планетах. Каждый из объектов, представляющих планеты Солнечной системы, содержит несколько параметров, а некоторые текстовые свойства — диаметр
ирасстояние от Солнца — мы определили как редактируемые. Изменение любого свойства перехватывается центральной функцией обработки событий, которую мы использовали для представления отладочной информации
встроке состояния браузера. (Возможность записывать данные в строку состояния в последних версиях Mozilla Firefox ограничена. В приложении А мы рассмотрим простую консоль, поддерживаемую средствами JavaScript, которая позволяет отображать сообщения о состоянии системы при отсутствии
вбраузере строки состояния.) Средства обработки событий почти идеально подходят для передачи серверу информации об обновлениях.
222 Часть II. Основные подходы к разработке приложений
Предположим, что на сервере выполняется сценарий updateDomainModel. jsp, который получает от клиента следующую информацию.
•Уникальный идентификатор планеты, информация о которой подлежит обновлению.
•Имя обновляемого свойства.
•Значение, присвоенное свойству.
Мы можем написать обработчик событий, который передавал бы серверу требуемую информацию.
function updateServer(propviewer)
{
var planetObj=propviewer.viewer.object; var planetId=planetObj.id;
var propName=propviewer.name; var val=propviewer.value; net.ContentLoader
(
'updateDomainModel.jsp', someResponseHandler, null, 'POST', 'planetId='+encodeURI(planetId) +'&propertyName='+encodeURI(propName)
+'&value='+encodeURI(val)
);
}
Этот обработчик надо связать с ObjectViewer. myObjectViewer.addChangeListener(updateServer) ;
Написать подобный код несложно, но результатом его выполнения будет передача по сети множества мелких фрагментов данных, что снижает эффективность работы. Для того чтобы получит контроль над трафиком, нам надо перехватывать обновления, помещать их в локальную очередь, а затем, во время бездействия приложения, передавать их серверу большими пакетами. Простая очередь для хранения информации об обновлениях, реализованная средствами JavaScript, показана в листинге 5.13.
ЛИСТИНГ 5.13. Объект CommandQueue
/ / О Создать обьехт очереди net.CommandQueue=function(id, u r l , freq)
{
t h i s . i d = i d ; net.cmdQueues[id]=this; this.url=url; this.queued=new Array(); this.sent=new Array(); if (freq)
{
this.repeat(freq);
}
}
Глава 5. Роль сервера в работе Ajax-приложения 223
// 0 Передать запрос серверу net.CommandQueue.prototype.addCommand=function(command){
if (this.isCommand(command))
{
this.queue.append(command,true);
}
}
net.CommandQueue.prototype.fireRequest=function(){ if (this.queued.length==0)
{
return;
}
var data="data=";
for(var i=0; i<this.queued.length;i++){ var cmd=this.queuedfi];
if (this.isCommand(cmd)){ data+=cmd.toRequestString(); this.sent[cmd.id]=cmd;
}
}
this .queued=new ArrayO; this.loader=new net.ContentLoader(
this.url, net.CommandQueue.onload,net.CommandQueue.onerro: "POST",data
);
}
// © Проверить тип объекта net.CommandQueue.prototype.isCommand=function(obj)
{
return
(
obj.implementsPropC'id")
&&obj.implementsFunc("toRequestString")
&&obj.implementsFunc("parseResponse")
);
}
// О Выполнить разбор ответа сервера net.CommandQueue.onload=function(loader)
{
var xmlDoc=net.req.responseXML;
var elDocRoot=xmlDoc.getElementsByTagName("commands")[0]; if (elDocRoot)
{
for(i=0;KelDocRoot.childNodes.length; i++)
{
elChild=elDocRoot.childNodes[i]; if (elChild.nodeName=="command")
224 Часть II. Основные подходы к разработке приложений
{
var attrs=elChild.attributes;
var id=attrs.getNamedItem("id").value; var command=net.commandQueue.sent[id]; if (command)
{
command.parseResponse(elChild);
}
}
}
}
}
net.CommandQueue.onerror=function(loader){ alert("problem sending the data to the server"); }
// © Опрос сервера net.CommandQueue.prototype.repeat=function(freq){
this.unrepeat(); if (freq>0){
this.freq=freq;
var cmd="net.cmdQueues["+this.id+"].fireRequest()"; this.repeater=setlnterval(cmd,freq*1000) ;
}
}
// 0 Отключить опрос сервера net.CommandQueue.prototype.unrepeat=function()
{
if (this.repeater)
{
clearlnterval(this.repeater) ;
}
this.repeater=null;
При инициализации О объекта CommandQueue (он назван так потому, что в очереди содержатся объекты Command) указывается уникальный идентификатор, URL сценария на стороне сервера и в качестве необязательного параметра — флаг, указывающий на необходимость повторного опроса. Алы тернативный вариант — активизация опроса вручную. Каждый из этих режимов может быть полезен, поэтому оба они предусмотрены в коде объекта. Когда очередь формирует запрос серверу, все содержащиеся в ней команды преобразуются в строки и передаются серверу ©.
В составе объекта содержатся два массива. Массив queued предполагает указание числовых индексов; в него помещаются новые обновления. Массив sent — ассоциативный. Он содержит те обновления, которые были отправлены серверу, но ответы на которые еще не были получены. В обоих массивах содержатся объекты Command, интерфейс которых определяется функцией isCommand() ©. Эти объекты обладают следующими свойствами.
Глава 5. Роль сервера в работе Ajax-приложения 225
•Объект может иметь уникальный идентификатор.
•Объект допускает сериализацию для включения в состав запроса POST ©.
•Объект может выполнять разбор ответа сервера О и определять, успешно ли был обработан запрос и должны ли предприниматься дальнейшие действия.
Для проверки выполнения условий используется функция implementsFunc(). Поскольку данный метод принадлежит базовому классу Object, может показаться, что он является стандартным инструментом JavaScript, но на самом деле мы объявляем функцию implements Func О в составе вспомогательной библиотеки.
Obj ect.prototype.implementsFunc=function(funcName){ return this[funcName] && this[funcName] instanceof
Function; }
Прототипы JavaScript подробно описаны в приложении Б. Теперь вернемся к объекту, реализующему очередь. Метод onload очереди О ожидает ответа сервера, содержащего XML-документ. В составе документа должны присутствовать дескрипторы <command>, помещенные в дескриптор <commands>.
Методы repeat () © и unrepeat () © используются для управления объектом таймера при периодическом опросе сервера.
Объект Command, предназначенный для обновления свойств объекта, описывающего планету, показан в листинге 5.14.
Листинг 5.14. Объект UpdatePropertyCommand planets.commands.UpdatePropertyCommand=function(owner,field,value)
{
this.id=this.owner.id+"_"+field; this.obj=owner; this.field-field; this.value=value;
}
planets.commands.UpdatePropertyCommand.toRequestString=
function()
{
return
{
type:"updateProperty",
id:this.id,
planetld:this.owner.id,
field:this.field,
value:this.value
}.simpleXmlifу("command");
}
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;
}
}