. Руководство пользователя Apache Struts

1.0 Предварительные требования

Данное Руководство пользователя написано для активных WEB-разработчиков и предполагает наличие практических знаний о том, как работают web-приложения на Java. Перед тем как начать, вы должны разбираться в основах следующих базовых технологий:

Последовательность вызовов HTTP Request/Response.

Канонический источник - RFC 2616 - Hypertext Transfer Protocol (HTTP/1.1).

Java сервлеты.

Хорошее место, с которого следует начать освоение этой технологии — Sun Servlet product page и Sun Java примеры.

JavaServer Pages (JSP).

Начните с Sun JSP product page и с Sun Java Tutorials.

JavaBeans.

Многие Struts-классы classes реализованы как JavaBeans. Если вы ранее не работали с JavaBeans, смотрите Sun JavaBean product page и Sun Java Tutorials

Если вы уже создавали WEB-приложения на других платформах, то скорее всего можете двигаться дальше по этому руководству и обращаться к указанным выше ссылкам при возникновении необходимости. Эти базовые технологии используются почти во всех областях Java Web приложений.

1.1 Введение: Вперед в прошлое! (или краткая история Struts)

Когда были изобретены Java сервлеты, многие программисты быстро осознали, что это Хорошая Вещь. Сервлеты были быстрее и мощнее стандартных CGI, переносимые и бесконечно расширяемые.

Но написание HTML-кода в бесконечных предложениях println() было утомительным и порождало проблемы. Ответом на эти трудности стали JavaServer Pages (JSP), которые перевернули написание написание сервлетов с ног на голову. Теперь разработчики легко смешивают HTML и Java код, и имеют все преимущества сервлетов.

Java web-приложения быстро стали “JSP-centric” - то есть “ориентированными на JSP”. Это само себе не было Плохой Вещью, но это мало помогало в разрешении проблем управления потоком выполнения приложений и порождало другие проблемы.

Очевидна, нужна была иная модель построения приложений...

Множество умных разработчиков осознало, что JSP И сервлеты могут быть использованы вместе для разработки и распространения web-приложений. Сервлеты могут помочь в управлении потоком выполнения приложений, а JSP помогут сфокусироваться на неприятном деле написания HTML-кода. В свое время совместное использование JSP и сервлетов стало известно как Model 2 (использование только JSP – Model 1).

Конечно, нет ничего нового под солнцем... и многие люди сразу указали на то, что Model 2 соответствует классическому паттерну проектирования Model-View-Controller, известному еще в языке Smalltalk. Теперь термины Model 2 и MVC являются взаимозаменяемыми терминами.

Проект Struts был запущен в мае 2000 года Craig R. McClanahan для того, чтобы обеспечить Java – разработчиков стандартной MVC-средой. В июле 2001 был выпущен релиз Struts 1.0, и, по нашему скромному мнению, Java Model 2 разработка никогда не будет прежней.

1.2 Паттерн проектирования Model-View-Controller ('MVC') Design Pattern

В паттерне MVC поток выполнения приложения всегда проходит через центральный контроллер. Контроллер перенаправляет запросы – в нашем случае, это HTTP запросы – к соответствующему обработчику. Обработчики привязаны к Модели, и каждый разработчик действует как адаптер между запросом и Моделью. Модель представляет, или инкапсулирует, бизнес-логику или состояние приложения. Управление обычно передается обратно через Контроллер соответствующему Представлению (View). Перенаправление осуществляется путем обращения к набору соответствий (mappings) между Моделью и представлением; причем этот набор обычно загружается из базы данных или из конфигурационного файла. Такая схема обеспечивает слабое связывание между Представлением и Моделью, что может сделать разработку и сопровождение приложения значительно проще.

1.3 Обзор среды Struts

Согласно паттерну Model-View-Controller, Struts имеет 3 основных компонента: сервлет контроллер, который входит в Struts, JSP страницы (это “представление”) и бизнес-логику приложения (“модель”). Давайте рассмотрим, как это все взаимодействует.

Struts'овский сервлет-контроллер связывает и перенаправляет HTTP запросы к другим объектам среды, включая JavaServer Pages и наследники класса org.apache.struts.action.Action , которые поставляются разработчиком приложения. Во время инициализации контроллер разбирает (parses) конфигурационный файл struts-config.xml. Конфигурационный файл определяет (помимо других вещей) соответствия org.apache.struts.action.ActionMapping s для данного приложения. Контроллер использует эти соответствия для превращения HTTP-запросов в действия приложения.

Обычно ActionMapping определяет:

Путь запроса (или “URI”)

тип объекта (наследника класса Action) для обработки запроса

и другие необходимые свойства

Объект Action может обработать запрос и ответить клиенту (обычно Web-браузеру), или указать, что управление должно быть передано куда-то дальше. Например, если проверка имени и пароля пользователя прошла успешно, Action для обработки этого события может пожелать перенаправить запрос на главное меню.

Объекты Action имеют доступ сервлету-контроллеру приложения, и таким образом имеют доступ к методам сервлета. Во время перенаправления запроса, объект Action может неявно перенаправить один или более совместно используемых (shared) объектов, включая JavaBeans, помещая их в одну из стандартных коллекций, которые совместно используются Java-сервлетами.

Объект Action может создать Bean, представляющий корзину покупателя, поместить этот bean в коллекцию сессии, и затем передать управление на другой mapping. Mapping'у может быть сопоставлен (mapping может использовать) JavaServer Page, отображающей содержимое корзины. Так как каждый клиент имеет собственную сессию, каждый из них будет иметь свою собственную корзину. В приложении Struts большинство элементов бизнес-логики может быть представлено с использованием JavaBeans. Объект Action может обращаться к свойствам JavaBean без углубления в детали реализации. Это инкапсулирует бизнес логику и таким образом Action может сфокусироваться на обработке ошибок и перенаправлении потока управления.

JavaBeans так же могут быть использованы для управления html-формами ввода (input forms). Ключевой проблемой при проектировании web-приложений является хранение и проверка того, что ввел пользователь, в промежутках между запросами. Используя Struts, вы можете определить свой собственный набор классов form bean, унаследовав их от o rg.apache.struts.action.ActionFor m , и легко сохранять данные для форм ввода в этих form bean. Эти beans сохраняются в одной из стандартных совместно используемых сервлетами коллекций, и могут быть использованы другими объектами, особенно объектами Action.

Form bean могут быть использованы в JSP для сбора данных, вводимых пользователем; объектами Action для проверки введенных пользователями данных; и опять в JSP для заполнения полей html-форм. В случае обработки ошибок, Struts имеет совместно используемый механизм для возбуждения и отображения сообщений об ошибках.

Struts form bean объявляются в конфигурационном файле, определяются в исходных Java-кодах, и привязываются к ActionMapping, используя общее имя свойства. Когда запрос вызывает Action, которое использует form bean, сервлет-контроллер либо извлекает, либо создает вновь form bean, и передает его объекту Action. Объект Action может затем проверить содержимое form bean перед тем, как форма ввода для данного form bean будет показана, а также поставить в очередь те сообщения, которые будут обработаны формой. Когда дело сделано, объект Action может вернуть управление, перенаправив его на форму ввода, обычно JSP. После этого контроллер может ответить на HTTP-запрос и направить клиента на JavaServer Page.

Среда Struts включает библиотеки специальных тэгов, которые могут автоматически поместить значения свойств form bean в поля html-форм. Единственная вещь, которую большинство JSP должны знать об остальной части среды это соответствующие имена полей и где надо сделать submit. Компоненты вроде сообщений, которые ставятся в очередь объектами Action могут быть отображены с помощью одного лишь тэга. Могут определяться другие тэги, зависящие от конкретного приложения, для того, чтобы скрыть детали реализации от JSP.

Специальные тэги среды Struts спроектированы для использования свойств интернационализации, которые встроены в платформу Java. Все метки, поля и сообщения могут извлекаться из ресурса сообщений (message resource), и Java может автоматически использовать правильный ресурс в зависимости от страны и языка клиента. Чтобы обеспечить сообщения на дополнительном языке, просто добавьте еще один файл ресурсов.

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

Для простейших приложений, объект Action иногда может обрабатывать бизнес-логику, связанную с запросом. Однако в большинстве случаев объект Action должен вызывать другой объект, обычно JavaBean, для выполнения операций реальной бизнес-логики. Это позволяет объектам Action сфокусироваться на обработке ошибок и управлении приложением, а не реализацией бизнес-логики. Чтобы быть легко переносимыми на другие платформы, JavaBean не должны ссылаться ни на какие объекты web-приложений. Объект Action должен извлекать/переводить необходимые детали из HTTP-запросов и передавать их объектам классов бизнес-логики в виде обычных Java-переменных.

В приложениях баз данных, например:

Bean бизнес-логики соединяется с базой данных и посылает запросы.

Bean бизнес-логики возвратит результат объекту Action

Объект Action сохранит результат в form bean в запросе (т.е. поместит form bean в context collection – прим.перев.)

JavaServer Page отобразит результат в виде HTML-формы

Никогда ни Action, ни JSP не нужно беспокоиться (или заботиться) о том, откуда пришел результат. Они должны знать как упаковать и показать его.

Остальная часть Руководства Пользователя более детально объясняет различные компоненты Struts. Релиз Struts также включает несколько Руководств разработчика, которые описывают различные аспекты среды, вместе с приложениями-примерами, стандартной API-документацией, и конечно, полными исходными кодами!

Struts распространяется под Apache Software Foundation license. Исходный код защищен законами от незаконного копирования, но бесплатен для использования в любых приложениях. Подробнее смотрите ASF license.

1.4 Model (Модель): Состояние системы и бизнес-логика в JavaBeans

Часть Model в системе, основанной на MVC, может быть под разделена на отдельные понятия – внутреннее состояние системы, и действия, которые могут предприниматься для изменения состояния. В терминах грамматики, мы должны думать о информации состояния как о существительных (вещах) и о действиях как о глаголах (изменение состояния этих вещей).

Обычно, ваше приложение будет представлять внутреннее состояние системы как набор одного или более JavaBean, со свойствами, которые представляют подробности состояния. В зависимости от сложности приложения, эти beans могут быть самодостаточны (и знать о том, как сохранять их состояние как-нибудь постоянно), или они могут быть фасадами, которые знают, как извлекать информацию из внешних источников (таких как базы данных), когда их запрашивают. Entity Enterprise JavaBeans (Entity EJBs) также обычно используются для представления внутреннего состояния.

Крупномасштабные приложения часто представляют набор возможных действий бизнес-логики как методы beans, обслуживающих информацию о состоянии. Например, вы можете иметь bean для корзины покупателя, сохраняемый в коллекции сессии для каждого текущего пользователя, со свойствами которые представляют текущий набор продуктов, которые клиент решил купить. Bean также может иметь метод checkOut(), который авторизует кредитную карту пользователя, и посылает заказ на склад, чтобы собрать его и доставить. Другие системы представляют доступные действия раздельно, например через Session Enterprise JavaBeans (Session EJBs).

В небольших приложениях , с другой стороны, возможные действия могут быть включены внутрь классов Action, которые являются частью роли Контроллера. Это нормально, когда логика очень простая, или там, где повторное использование бизнес-логики в других средах не предусматривается. Среда Struts поддерживает любой из этих подходов к разработке приложений, но сильно рекомендуется отделять бизнес-логику (“как это делается”) от той роли, которую играют классы Action (“что делать”).

1.5 View (Представление): JSP страницы и компоненты для представления

View-часть приложения на Struts обычно строится с использованием технологии JavaServer Pages (JSP). JSP-страницы могут содержать статический HTML (или XML) текст, называемый “текстом шаблона” (template text), плюс возможность вставлять динамического содержания, основанного на интерпретации (в момент вызова запроса) специальных тэгов действий (action tags). Среда JSP включает набор стандартных тэгов действий, таких как <jsp:useBean>; их назначение описано в спецификации JavaServer Pages Specification. Помимо этого, предоставляется стандартное средство для определения своих собственных тэгов, которые организуются в “специальные библиотеки тэгов”.

Struts включает расширенную библиотеку тэгов, которые облегчает создание пользовательских интерфейсов, которые полностью интернационализованы, и которые грациозно взаимодействуют с ActionForm beans, являющимися Model-частью системы. Использование этих тэгов подробно обсуждается позже.

В добавление к JSP-страницам, Action и библиотекам специальных тэгов, для бизнес-объектов часто необходимо преобразовать их в HTML (или XML), основываясь на текущем состоянии системы в момент запроса. Преобразованное представление таких объектов может быть легко включено в результирующие JSP при помощи стандартного тэга действия <jsp:include>.

1.6 Controller( Контроллер ): ActionServlet и ActionMapping

Часть Контроллер приложения фокусируется на получении запросов от клиента (обычно пользователя, работающего через web-браузер), принятии решения о том, какая функция бизнес-логики должна быть выполнена, и затем передача ответственности за производство следующей фазы пользовательского интерфейса на соответствующий View-компонент. В Struts главный компонент Контроллера – это сервлет класса ActionServlet. Этот сервлет конфигурируется с помощью определения набора ActionMappings. ActionMapping определяет путь, который соотвествует запрошенному URI из входящего запроса, и обычно определяет полностью заданное имя класса Action. Все Actions унаследованы от org.apache.struts.action.Action. Actions инкапсулируют бизнес-логику, интерпретируют результат, и окончательно передают управление к соответствующему View-компоненту для формирования ответа.

Struts также поддерживает возможность использовать ActionMapping классы, которые имеют дополнительные свойства помимо стандартных, требуемых для функционирования среды. Это позволяет сохранять дополнительную информацию, требуемую для вашего приложения, но в то же время пользоваться остальными функциями среды. Кроме того, Struts позволяет вам определять логические “имена”, используемые для передачи управления на них – таким образом, метод может запросить страницу “MainMenu” (т.е. Главное меню), не имея понятия о том, на какой в действительности JSP странице располагается главное меню.

Эти функции очень здорово помогает в отделении логики управления (что делать) от логики представления (как это показывать).

Работка Model-компонентов

2.1 Обзор

Обычно много внимания при разработке web-приложений уделяется View. Однако, вы должны гарантировать, что обработка каждого посланного пользователем запроса также ясно определена с точки зрения Model. Обычно разработчик Model-компонентов сосредотачивается на создании JavaBean-классов, которые поддерживают функциональные требования. Точные характеристики beans, требуемых конкретным приложением могут сильно меняться в зависимости от этих требований, они они в общем могут быть классифицированы и подразделены на несколько категорий, обсуждаемых ниже в этом разделе. Однако первым делом мы рассмотрим краткую концепцию термина “scope” (англ. область) и его связь с beans.

2.2 JavaBeans и Scope

Внутри web-приложений объекты JavaBeans могут сохраняться в (и извлекаться из) наборе различных коллекций “атрибутов”. Каждая коллекция имеет различные правила, регламентирующие время жизни коллекции и видимость объектов-beans, помещенных в нее. В совокупности, правила времени жизни и видимости beans называется scope (областью видимости) beans. Спецификация JavaServer Pages (JSP) определяет различные варианты scope, используя следующие термины (эквивалентные концепции из Servlet API приведены в круглых скобках):

pagebeans которые видимы в пределах единственной JSP-страницы, в течение времени жизни текущего запроса (это локальные переменные метода service())

request - beans, которые видны на единственной JSP странице, а также любой странице и сервлете, который включен в эту страницу, или перенаправлен с этой странице (атрибуты запроса (Request attributes))

session beans, которые всем JSP страницам и сервлетам, которые участвуют в конкретной сессии пользователя, в одном и более запросах (атрибуты сессии)

application beans, которые видимы всем JSP-страницам и сервлетам, являющимся частью web-приложения (Servlet context attributes)

Важно помнить о том, что сервлеты и JSP-страницы в одном web-приложении совместно используют тот же самый набор коллекций beans. Например, bean, сохраненный в атрибуте запроса (request attribute) в сервлете примерно так:

MyCart mycart = new MyCart(...);

request.setAttribute("cart", mycart);

немедленно становится видимым на JSP-странице, на которую Сервлет передал управление, используя стандартный тэг:

<jsp:useBean id="cart" scope="request"

class="com.mycompany.MyApp.MyCart"/>

2.3 ActionForm Beans

Примечание: ActionForm beans на самом деле ближе к View, чем к Model.

При использовании Struts обычно предполагается, что необходимо определить ActionForm bean (то есть, Java-класс, расширяющий класс ActionForm) для каждой формы ввода, требуемой для вашего приложения. ActionForm beans иногда называют просто “form beans” (beans для формы). Если вы определяете такие beans в конфигурационном файле ActionMapping (см. " Разработка компонентов Controller"), то сервлет-контроллер Struts автоматически выполнит для вас следующие действия, прежде чем вызвать соответствующий Action:

Проверяет, нет ли в пользовательской сессии экземпляра bean соответствующего класса, с соответствующим ключом.

Если в сессии нет такого Bean, то создается новый экземпляр и автоматически добавляется к пользовательской сессии (ес-но, со scope = session)

Для каждого параметра запроса чье имя соответствует имени свойства в bean, вызывается соответствующий setter-метод (т.е. setXXX, см. спецификацию JavaBeans). Это действует примерно так же, как стандартное действие для JSP <jsp:setProperty>, когда вы используете wildcard “*” для того, чтобы выбрать все параметры.

Обновленное ActionForm bean передается в метод perform() класса Action во время его вызова, обеспечивая немедленный доступ к его значениям

Когда вы будете программировать ваши ActionForm beans, старайтесь соблюдать следующие принципы:

Сам по себе ActionForm класс не требует реализации никаких специфических методов. Он используется для определения роли, которую данное конкретное bean играет во всеобщей архитектуре. Обычно ActionForm bean имеет только getter и setter методы для своих свойств - и никакой бизнес-логики.

Объект ActionForm также предлагает стандартный механизм проверки (standard validation mechanism). Если вы переопределите заглушку для метода и обеспечите сообщения об ошибках в стандартном ресурсе приложения, то Struts автоматически проверит данные, вводимые в форме (используя ваш метод). Подробнее смотрите "Автоматическая проверка Form". Конечно, вы также можете проигнорировать проверку в ActionForm и обеспечить свою собственную в объекте Action.

Определяйте свойство (с соответствующими методами setXxx() и getXxx()) для каждого поля в html-форме. Имена полей и свойств должны соответствовать друг другу согласно обычным соглашениям JavaBean. Например, для поля с именем username будет вызван метод setUsername().

Представляйте ваши ActionForm beans как стену (firewall) между HTTP и Action. Используйте проверку для того, чтобы гарантировать наличие всех требуемых свойств и то, что они содержат разумные значения. ActionForm, которая не прошла проверку, не будет даже передана в Action для обработки.

Вы можете расположить экземпляр bean на вашей форме и использовать вложенные ссылки на свойства. Например, вы можете иметь beancustomer” в вашем ActionForm, и затем обращаться к свойству "customer.name" на JSP. Это будет соответствовать вызову методов customer.getName() и customer.setName(string Name) в вашем customer bean. Подробнее о синтаксисе вложенных свойств смотрите Tag Library Developer Guides (Руководства по использованию библиотек тэгов).

Предупреждение: Если вы вкладываете существующий экземпляр bean в вашу форму (имеется в виду ActionForm, конечно), то подумайте о свойствах, которые bean предоставляет. Любое public-значение в ActionForm, которое принимает String-значение, может быть установлено с помощью строки запроса (query string). Может быть полезно помещать подобные beans в “тонкую обертку”, которая будет предоставлять (открывать) только необходимые в данном случае свойства. Также эта обертка может обеспечивать фильтр, для того чтобы быть уверенным в том, что свойства во время выполнения не установлены в неподходящие значения. (Прим. перев.: данное предупреждение относится прежде всего к вопросам безопасности – необходимо помнить о том, что HTTP request может быть сформирован не только в вашей JSP, а также другими средствами – с целью хищения информации, например, и поэтому надо всегда проверять входные данные на адекватность)

Вы должны заметить, что “form” (форма) в том смысле, как это обсуждается здесь, не обязательно соответствует единственной JSP-странице в пользовательском интерфейсе. Обычное дело во многих приложениях – это иметь “form” (c точки зрения пользователя), которая “растянута” по многим страницам. Подумайте, например, о стиле пользовательского интерфейса в виде мастеров (wizard style user interface), который обычно используется во время установки нового программного обеспечения. Struts дает возможность определить единственную ActionForm bean, которое содержит свойства для всех полей, вне зависимости от того, на какой странице они в действительности будут отображены. Аналогично, различные страницы для одной и той же формы должны быть переданы на обработку (submitted) к одному и тому классу Action.

Если вы следуете этим предложениям, то дизайнеры могут изменять порядок расположения полей на и между различными страницами, и часто без изменения логики обработки.

2.4 System State Beans (Beans состояния системы)

Реальное состояние системы обычно представлено как набор из нескольких JavaBeans классов, чьи свойства определяют текущее состояние. Система корзины покупателя, например, будет включать bean для представления корзины, который будет храниться для каждого отдельного покупателя и будет (помимо других вещей) включать набор тех продуктов, которые покупатель в данный момент отобрал для покупки. Отдельно, система будет включать различные beans для хранения информации о профиле пользователя (включая его кредитную карту и ShipTo адрес), так же как и каталог доступных продуктов и их текущие количества на складе.

В небольших системах, или когда информацию о состоянии не нужно хранить длительное время, набор beans состояния системы может содержать все знания, которые система когда-либо имеет. Или, как это часто случается, beans состояния системы представляют информацию, которая постоянно хранится в некой внешней базе данных (например, объект CustomerBean который соответствует конкретной строчке в таблице CUSTOMERS), и создаются и уничтожаются из хранилища на сервере когда это необходимо. В очень больших приложениях для этих целей используются Entity Enterprise JavaBeans.

2.5 Business Logic Beans (Beans бизнес-логики)

Вы должны инкапсулировать функциональную логику вашего приложения в виде вызовов методов классов JavaBeans, спроектированных для этой цели. Эти методы могут быть частью тех же классов, которые используются для Beans состояния системы, или же они могут быть в отдельных классах, выделенных для выполнения логики. В последнем случае, как правило, вам необходимо будет передавать beans состояния системы в качестве параметров.

Чтобы обеспечить максимальное повторное использование кода, beans бизнес-логики должны быть спроектированы и реализованы так, чтобы они ничего не знали о том, что они запускаются в среде web-приложения. Если вы импортируете классы из javax.servlet.* в ваш bean, то вы привязываете эту бизнес логику к среде выполнения web-приложений. Рассмотрите вопросы перестройки (rearranging things), которые выполняют ваши Action классы (часть роли Контроллера, как описано ниже): переводят всю необходимую информацию из HTTP-запроса в вызовы getter и setter-методов свойств ваших beans бизнес-логики, после чего можно вызвать метод execute(). Подобные бизнес-классы могут быть повторно использованы не только в web-приложениях, для которых они первоначально создавались.

В зависимости от сложности и масштаба вашего приложения, beans бизнес-логики могут быть обычными JavaBeans, которые взаимодействуют с beans состояния системы, передаваемые как в качестве параметров, или обычными JavaBeans, которые обращаются к базе данных используя вызовы JDBC. Для крупных приложений, вместо этих beans часто используются stateful или stateless (с сохранением состояния или без сохранения состояния)Enterprise JavaBeans (EJBs).

2.6 Доступ к реляционным базам данных

Struts могут определять datasources (источники данных) для приложения в своем стандартном конфигурационном файле. Также предусмотрен простой пул JDBC-соединений. Подробнее смотрите Конфигурационный файл Action Mappings, и Utilities Developer Guide.

После определения datasource, можно воспользоваться примером установления соединения внутри метода perform() класса Action.

public ActionForward

perform(ActionMapping mapping,

ActionForm form,

HttpServletRequest request,

HttpServletResponse response)

{

try {

javax.sql.DataSource dataSource =

servlet.findDataSource(null);

java.sql.Connection myConnection =

dataSource.getConnection();

//делайте что вам нужно с экземпляром myConnection

} catch (SQLException sqle) {

getServlet().log("Connection.process", sqle);

} finally {

//завершает все это блок finally, который

//гарантирует, что connection закрыт

try {

myConnection.close();

} catch (SQLException e) {

getServlet().log("Connection.close", e);

}

}

}

Обратите внимание, что общий пул соединений Struts являет опциональным компонентом. Множество приложений на базе Struts использует другие пулы соединений для получения наилучшей производительности, особенно на промышленных системах большого объема.

Разработка View-компонентов

3.1 Обзор

Table of Contents

Эта глава посвящена задаче построения View-компонентов приложения, которые в основном создаются с использованием технологии JavaServer Pages (JSP). В частности, Struts обеспечивает поддержку интернационализованных приложений, так же как и взаимодействие с формами ввода. Также здесь коротко рассматриваются несколько других разделов, связанных с View-компонентами.

3.2 Интернационализация

Controller Components

Несколько лет назад разработчики приложений могли рассчитывать на распространение своих приложений только среди жителей собственной страны, которые используют один (иногда два) языка и единственный способ представления таких вещей, как даты, числовые и денежные значения. Однако взрывной рост разработки приложений с использованием web-технологий, также как и распространение этих приложений через Интернет, частично сделал национальные границы невидимыми. Это привело к необходимости разрабатывать приложения с поддержкой интернационализации (часто интернационализацию по-английски называют i18n (ай-эйтин), потому что в слове internationalization 18 букв между i и n) и локализации (localization).

Struts строится на основе платформы Java, чтобы обеспечить возможности построения интернационализованных и локализованных приложений. Ниже приведены ключевые концепции, с которыми нужно познакомиться:

Locale – основной Java класс, который поддерживает интернационализацию - java.util.Locale. Каждая Locale представляет собой конкретный выбор страны и языка (плюс возможный вариант языка), а также набор способов форматирования таких вещей как числа и даты.

ResourceBundle - Класс java.util.ResourceBundle обеспечивает основные инструменты для поддержки сообщений на многих языках. Подробнее смотрите Javadocs – класс ResourceBundle, а также информацию об интернационализации в документации к вашему релизу JDK.

PropertyResourceBundle – Одна из стандартных реализаций ResourceBundle позволяет вам определять ресурсы, используя тот же синтаксис “имя=значение” ("name=value"), что используется при инициализации файлов свойств. Это очень удобно для подготовки пакетов ресурсов (resource bundles) с сообщениями, которые используются в web-приложении, потому что обычно такие сообщения представлены в виде текста.

MessageFormat - Класс java.text.MessageFormat позволяет заменять части строки сообщения (в этом случае они извлекаются из пакета ресурсов) аргументами, определяемыми во время выполнения. Это полезно в случае, когда вы создаете предложение, но слова в нем будут появляться в различном порядке на различных языках. Строка-заменитель {0} в сообщении заменяется первым аргументом, {1} заменяется вторым аргументом и так далее.

MessageResources – Класс Struts org.apache.struts.util.MessageResources позволяет обращаться с пакетами ресурсов как с базой данных, и позволяет запрашивать конкретную строку сообщения для конкретной Locale (обычно Locale для текущего пользователя) вместо Locale, используемой по умолчанию на сервере, где запущено данное приложение.

Обратите внимание, что поддержка i18n в средах вроде Struts ограничена представлением интернационализованного текста и изображений для пользователя. Поддержка для Locale соответствующих методов ввода (используемых в таких языках как японский, китайский и корейский) возлагается на клиентское устройство – обычно web-браузер.

Что получить интернационализованное приложение, следуйте рекомендациям, описанным в документе по интернационализации в документации JDK на вашей платформе, для того чтобы создать файл свойств (properties file), содержащий сообщения для каждого языка. Пример рассмотрен чуть ниже.

Предположим, что ваш исходный код создается в package com.mycompany.mypackage, и таким образом сохраняется в каталоге (относительно вашего каталога исходных кодов) com/mycompany/mypackage. Чтобы создать пакет ресурсов, названный com.mycompany.mypackage.MyResources, вы должны создать следующие файлы в каталоге com/mycompany/mypackage:

MyResources.properties – содержит сообщения на языке, используемом по умолчанию на вашем сервере. Если ваш язык по умолчанию – English, то в этом файле должны содержатся строки вроде этой: prompt.hello=Hello

MyResources_xx.properties – содержит те же самые сообщения на языке, чей ISO код = “xx” (вы найдете ссылку на список текущих языков на странице ResourceBundle Javadoc). Для французской версии сообщения, показанного выше, вы должны написать что то вроде: prompt.hello=Bonjour. Вы можете иметь пакеты ресурсов для каждого языка, который вам нужно поддерживать.

Когда вы конфигурируете сервлет-контроллер в дескрипторе для распространения web-приложения (web application deployment descriptor), одна из тех вещей, которые вы должны определить в параметрах инициализации – основное имя пакета ресурсов для вашего приложения. В случае, описанном выше, это должен быть com.mycompany.mypackage.MyResources:

<servlet>

<servlet-name>action</servlet-name>

<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>

<init-param>

<param-name>application</param-name>

<param-value>com.mycompany.mypackage.MyResources</param-value>

</init-param>

<.../>

</servlet>

Важно отметить, что пакет ресурсов должен находиться в class path для вашего приложения. Другой подход – сохранять MyResources.properties в каталоге классов вашего приложения. Затем вы можете просто определить "myResources" как значение в вашем приложении (as the application value). Просто будьте осторожны, чтобы этот файл не был удален вашим build-скриптом, который удаляет классы как часть "clean" target.

Если вы пользуетесь таким методом, вот Ant task, который нужно запустить во время компиляции вашего приложения, который скопирует содержимое каталога src/conf в каталог классов:

<!-- Copy any configuration files -->

<copy todir="classes">

<fileset dir="src/conf"/>

</copy>

3.3 Взаимодействие Forms и FormBean

Так или иначе, большинство разработчиков веб-приложений должны создавать формы, используя стандартные возможности HTML, такие как тэг <input>. Пользователи ожидают, что интерактивные приложения должны иметь определенное поведение, и одно из этих ожиданий связано с обработкой ошибок – если пользователь делает ошибку, приложение должно позволить ему исправить лишь то, что нужно – без необходимости вводить заново остальную (правильную) часть информации на текущей странице или форме.

Выполнение этого требования пользователей является скучным и громоздким делом, когда мы кодируем лишь с помощью стандартного HTML и JSP. Например, элемент ввода для поля username в JSP должен выглядеть примерно так:

<input type="text" name="username"

value="<%= loginBean.getUsername() %>"/>

это трудно правильно напечатать, смущает HTML разработчиков, которые не опытны в концепциях программирования , и могут привести к проблемам с редакторами HTML. Вместо этого Struts обеспечивает исчерпывающий набор средств для создания форм, основываясь на средствах специальной библиотеки тэгов (Custom Tag Library) в JSP 1.1. Пример, показанный раньше, может быть представлен средствами Struts следующим образом:

<html:text property="username"/>

без всякой необходимости явно ссылаться на JavaBean, из которого извлекается первоначально значение. Это автоматически обрабатывается средой.

HTML-формы иногда используются для загрузки других файлов (upload other files). Большинство браузеров поддерживают это с помощью элемента <input type="file">, который генерирует кнопку поиска файлов (file browse button), но на совести разработчика остается то, как обработать вхлдящие файлы. Struts обрабатывает такие "multipart"-формы таким же способом, как и обычные формы. В следующем разделе мы рассмотрим использование Struts для создания простой формы входа (login form), а также простую mulitpart-форму.

3.3.1 Разработка Forms в Struts

Полный пример login-формы иллюстрирует то, что Struts взаимодействует с формами гораздо менее болезненно, чем при прямом использовании HTML и стандартных средств JSP. Рассмотрим следующую страницу logon.jsp (основанную на примере приложения, включенном в поставку Struts):

<%@ page language="java" %>

<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>

<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>

<html:html>

<head>

<title>

<bean:message key="logon.title"/>

</title>

<body bgcolor="white">

<html:errors/>

<html:form action="/logon" focus="username">

<table border="0" width="100%">

<tr>

<th align="right">

<html:message key="prompt.username"/>

</th>

<td align="left">

<html:text property="username" size="16"/>

</td>

</tr>

<tr>

<th align="right">

<html:message key="prompt.password"/>

</th>

<td align="left">

<html:password property="password" size="16"/>

</td>

</tr>

<tr>

<td align="right">

<html:submit>

<bean:message key="button.submit"/>

</html:submit>

</td>

<td align="right">

<html:reset>

<bean:message key="button.reset"/>

</html:reset>

</td>

</tr>

</table>

</html:form>

</body>

</html:html>

Следующие пункты проясняют ключевые свойства обработки форм в Struts, основываясь на приведенном примере:

Директива taglib говорит компилятору JSP-страницы, где найти дескриптор библиотеки тэгов (tag library descriptor) для библиотеки тэгов Struts. В данном случае, мы используем префикс “bean” (prefix="bean") который идентифицирует тэги из библиотеки struts-bean, и “html” как префикс, который идентифицирует тэги из библиотеки struts-html. Вы можете использовать любой удобный для вас префикс для идентификации тэгов библиотеки.

На этой странице несколько раз появляется тэг message для того, чтобы извлечь интернационализованные сообщения из объекта MessageResources, содержащего все ресурсы данного приложения. Чтобы данная страница могла работать, следующие ключи сообщений должны быть определены в ресурсах:

logon.title – заголовок данной страницы входа

prompt.username – строка приглашения для имени пользователя- "Username:"

prompt.password - строка приглашения пароля: "Password:"

button.submit - надпись на кнопке: "Submit"

button.reset - надпись на кнопке:"Reset"

Когда пользователь войдет, приложение может сохранить объект Locale в сессии пользователя. Эта Locale будет использоваться для выбора сообщений на соответствующем языке. Это делает проще реализовать возможность переключения языков – просто измените сохраненный объект Locale, и все сообщения переключатся на нужный язык автоматически.

Тэг errors показывает любые сообщения об ошибках, которые были сохранены компонентами бизнес-логики, или ничего не показывает, если никаких ошибок не было записано. Этот тэг будет более подробно описан ниже.

Тэг form представляет HTML элемент <form>, основываясь на заданных атрибутах. Он также связывает все поля этой формы с объектом FormBean, хранящемся в сессии под ключем logonForm. Разработчики Struts-приложения должны обеспечить Java-реализацию этого form bean, унаследовав его от класса Struts ActionForm. Этот bean используется для задания начальных значений всем полям ввода, которые имеют имена, соответствующие именам свойств в этом bean. Если соответствующий bean не найден, то новый экземпляр будет создан автоматически, используя указанное имя Java-класса. (Form beam может быть так же определен в конфигурационном файле Struts, и в этом случае атрибуты Name и Type могут быть опущены. Подробнее смотрите Конфигуарционный фай Action Mappings.)

Тэг text представляет HTML элемент <input> с типом “text”. В этом случае также указывается число символов (длина), которое будет иметь данный элемент при отображении в браузере. Когда страница генерируется, этот элемент получит значение свойства username d соответствующем bean (то есть, значение, которое будет возвращено getUsername()).

Тэг password используется аналогично. Различие в том, что браузер будет показывать вместо вводимых символов звездочки, когда пользователь будет набирать свой пароль.

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

Обработка multipart-формы также очень проста. Очевидно, когда вы создаете multipart-форму, вы создаете форму, в которой есть хоть один элемент типа “file” (<input type=”file”>). Первым шагом в создании multipart-формы является использование на JSP-странице библиотеки тэгов Struts struts-html:

<%@page language="java">

<%@taglib uri="/WEB-INF/struts-html.tld" prefix="html">

<html:form action="uploadAction.do">

Please Input Text:

<html:text property="myText"><br/>

Please Input The File You Wish to Upload:<br/>

<html:file property="myFile"><br />

<html:submit />

</html:form>

Следующим шагом будет создание вашего ActionForm bean:

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;

import org.apache.struts.action.ActionMapping;

import org.apache.struts.upload.FormFile;

public class UploadForm extends ActionForm {

protected String myText;

protected FormFile myFile;

public void setMyText(String text) {

myText = text;

}

public String getMyText() {

return myText;

}

public void setMyFile(FormFile file) {

myFile = file;

}

public FormFile getMyFile() {

return myFile;

}

}

Взгляните на Javadocs для FormFile, чтобы ознакомиться с методами, которые он представляет для манипуляции файлами при их загрузке (file uploading). Также взгляните на Javadocs для ActionServlet и ActionMapping, чтобы ознакомиться с различными параметрами, которые можно задавать, чтобы изменить процесс загрузки файлов. Обычно в методе perform() в вашем Action-классе вы должны вызвать ((UploadForm) form).getMyFile() , чтобы получить FormFile и делать с ним все, что пожелаете.

3.3.2 Поддерживаемые типы Input-полей

Struts определяет HTML-тэги для следующих типов input-полей (ссылки ведут на соответствующую справочную информацию):

checkboxes

hidden поля

password input- поля

radio кнопки

reset кнопки

select списки с встроенными опциями

option

options

submit кнопки

text input-поля

textareas

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

3.3.3 Другие полезные тэги для представления данных

Есть еще несколько полезных тэгов для создания представлений. Обращайтесь к документации по каждой конкретной библиотеке тэгов вместе с изучением Tag Developers Guides.

[logic] iterate повторяет для каждого элемента коллекции (Hashtable, Map. etc) тело тэга. Это полезно для организации цикла по всем элементам коллекции.

[logic] present в зависимости от того, какой атрибут установлен, это тэг проверяет текущий request, и выполняет тело тэга, если в request'е присутствует это значение. Только один из атрибутов может использован при каждом использовании тэга, а если вы используете атрибут-свойство, то в этом случае также требуется указать имя атрибута. Возможные виды атрибуты включают cookie, header, name, parameter, property, role, scope, и user.

[logic] notPresent этот тэг-антоним для present. Тэг notPresent обеспечивает ту же функциональнось, что и present, но только в случае, если указанный атрибут не присутствует в request. Тэг полезен, например, для проверки того, зарегистрировался ли пользователь в системе – и если нет, то перенаправляем управление на страницу входа.

[html] link генерирует HTML-элемент <a> как anchor definition или как гиперссылку на указанный URL, и автоматически указывает кодировку URL чтобы управлять состоянием сессии в случае отсутствии поддержки cookies.

[html] img генерирует HTML <img> элемент с возможностью динамически модифицировать URL, определенный атрибутами "src" и "lowsrc" – подобно тому, как это делает тэг <html:link>.

[bean] parameter извлекает значение указанного параметра запроса (request parameter) и определяет результат как атрибут page scope с типом String или String[].

3.3.4 Автоматическая проверка Form

Помимо взаимодействия форм и beans, описанного выше, Struts предлагает дополнительные средства для проверки значений полей ввода, которые были получены от пользователя. Чтобы использовать данные средства, переопределите (override) следующий метод в вашем классе-наследнике ActionForm:

public ActionErrors validate(ActionMapping mapping, HttpServletRequest request);

Метод validate() вызывается сервлетом-контроллером после того, как свойства bean были заполнены и с помощью submit были посланы на сервер, но перед тем, как будет вызван соответствующий метод perform() action-класса. Метод validate() имеет следующие опции:

The validate() method is called by the controller servlet after the bean properties have been populated, but before the corresponding action class's perform() method is invoked. The validate() method has the following options:

Если выполнили соответствующие проверки и не нашли никаких проблем – то возвращаем либо Null, либо экземпляр ActionErrors, имеющий нулевую длину, и в этом случае сервлет-контроллер вызовет метод perform() в соответствующем Action-классе.

Если выполнили соответствующие проверки и нашли проблемы – то возвращаем экземпляр коллекции ActionErrors, содержащий набор экземпляров ActionError (без -s), где ActionError – это класс, который содержит ключи сообщений об ошибках (ключи эти расшифрованы, конечно, в пакете MessageResources приложения), которые должны быть показаны. Сервлет-контроллер сохранит этот массив ошибок в атрибуте request, удобном для использования в тэге <html:errors>, и затем возвратит управление обратно ту же форму ввода (которая идентифицируется как свойство input для данного ActionMapping). В результате получается удобное средство для обработки ошибочных ситуаций, которое дает пользователю возможность вернуться в то место, где он ошибся и исправить ошибку.

Как было указано выше, эта автоматическая проверка полей ввода является полностью опциональной. Реализация метода validate() по умолчанию возвращает null, и сервлет-контроллер предполагает, что вся требуемая проверка будет производится в action-классе.

Обычный подход – это выполнять первичные, простейшие проверки с использованием метода ActionForm validate(), а затем производить проверку с точки зрения бизнес-логики в action-классе.

Дополнительный пакет для выполнения проверок в ActionForm доступен в Nightly Build (но не забудьте проверить и текущий релиз!) и с сайта David Winterfeldt's Web site.

3.4 Другие методы представления данных

Хотя стиль вашего приложения (look and feel) может быть полностью построен на основании стандартных возможностей JSP и специфических библиотек тэгов Struts, вы можете рассмотреть возможность использования других методов, которые улучшат повторное использование компонентов, облегчат эксплуатацию приложения, и/или уменьшат количество ошибок. Несколько возможных вариантов рассмотрены ниже в этом разделе.

3.4.1 Тэги для конкретного приложения

Помимо использования библиотек тэгов, поставляемых вместе со Struts, легко создавать тэги, которые нужны для приложения, которое вы строите, чтобы облегчить создание пользовательского интерфейса. Пример приложения, включенный в Struts, иллюстрирует принципы создания таких тэгов, на базе следующих примеров тэгов:

checkLogon – этот тэг проверяет существование конкретного объекта сессии, и передает управление на страницу logon, если объекта сессии не существует. Это используется для того, чтобы отловить ситуацию, когда пользователь добавил страницу из “середины” вашего приложения в закладки и пытается попасть на эту страницу, миновав диалог входа, или когда время сессии истекло.

linkSubscription – создает гиперссылку для страницы, содержащей сведения о Subscription, которое передает требуемый первичный ключ как атрибут request'а. Это используется для вывода списка подписок для данного пользователя, и чтобы создать ссылки для их редактирования или удаления.

linkUser – генерирует гиперссылку на страницу подробностей о пользователе, передавая требуемый первичный ключ как атрибут запроса.

Исходный код для этих тэгов находится в каталоге src/example, в package org.apache.struts.example, вместе с другими Java-классами, используемыми в данном примере.

3.4.2 Сборка страниц с помощью директивы Includes

Создание целого представления страницы в одном JSP-файле (c использованием тэгов и beans для доступа к требуемым динамическим данным) является очень распространенным подходом к проектированию, и используется в приложении-примере, включенном в поставку Struts. Однако, многие приложения требуют отображать множество логически отличающихся частей вашего приложения вместе на одной странице.

Например, приложение-портал должно иметь несколько (или даже все) следующие функциональные возможности на “домашней” странице портала:

Доступ к поисковому механизму данного портала.

Одну или более “новостных лент”, с разделами по интересам согласно пользовательскому профилю.

Доступ к форумам этого, связанных с разделами.

“Индикатор ожидания почты”, если ваш портал поддерживает бесплатные email.

Разработка различных сегментов сайта становится проще, если вы разделите свою работу, и назначите различных разработчиков на различные сегменты. Затем можно будет использовать возможность include в технологии JavaServer Pages для того чтобы объединить результаты в единую результирующую страницу, или использовать тэг include, поставляемый вместе со Struts. Есть три возможных типа include, зависящих от того, когда нужно получить комбинацию результатов:

Директива <%@ include file="xxxxx" %> может подключить файл, который содержит Java-код или JSP-тэги. Код во включаемом файле может даже ссылаться на переменные, объявленные выше во внешней JSP-странице. Код встраивается (inlined) в другую JavaServer Page до того, как он компилируется, и таким образом он определенно содержать нечто большее, чем просто HTML.

include action (<jsp:include page="xxxxx" flush="true" />) обрабатывается во время запроса, и управляется прозрачно для сервера. Помимо всего прочего, это означает, что вы можете выполнять условную сборку страницы из подключений, если вложите их в тэг вроде equals.

Тэг bean:include принимает в качестве аргумента либо “forward”, представляющий логическое имя, связанное с подключаемой jsp, либо аргумент “id”, который представляет String-переменную context, используемую для вывода jsp-страницы.

Другим подходом может быть использование библиотеки тэгов шаблонов Struts (Struts Template Tag library). Подробнее смотрите Руководство Разработчика.

Использование Tiles является альтернативой первоначальной библиотеки Template Tag. Tiles предлагают несколько расширений и новых возможностей. Tiles доступны в Nightly Build (но не забудьте проверить и текущий релиз!) и с сайта Cedric Dumoulin's Web site.

3.4.3 Компоненты обработки изображений

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

Представить гиперссылку с URL, который выполнит запрос сервлета. Сервлет будет использовать графическую библиотеку чтобы представить графическое изображение, соответственно установить тип содержимого (content type) - такое, как image/gif, и возвратить сгенерированные байты обратно браузеру, который покажет точно так же, как если бы он получил статический файл.

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

3.4.4 Представление текста

Некоторые приложения требуют использование динамически сгенерированного текста или разметки, например XML. Если целая страница представляется таким образом, и есть возможность использовать PrintWriter для получения вывода, то очень просто сделать это из Action:

response.setContentType("text/plain"); // or text/xml

PrintWriter writer = response.getWriter();

// use writer to render text

return(null);

4 Разработка Controller- компонентов

4.1 Обзор

TR

Теперь, когда вы понимаете, как создавать компоненты Model и View, настало время разобраться с компонентами Controller. Struts включает в себя сервлет, который реализует основную функцию сопоставления URI запроса и соответствующего Action-класса. Следовательно, существуют следующие основные правила связанные с реализацией Controller:

написать Action-класс для каждого логического запроса, который может быть получен (этот класс должен расширять org.apache.action.Action)

Сконфигурировать ActionMapping (в виде XML) для каждого логического запроса, который может быть послан (submit). XML-конфигурационный файл обычно называется struts-config.xml.

Обновить файл описания для приложения (web application deployment descriptor file) в виде XML для вашего приложения и включить туда необходимые компоненты Struts

Добавить соответствующие компоненты Struts в ваше приложение

4.2 Классы Action

Класс Action определяет 2 метода, которые могут исполняться в зависимости от вашей сервлет-среды:

public ActionForward perform(ActionMapping mapping,

ActionForm form,

ServletRequest request,

ServletResponse response)

throws IOException, ServletException;

public ActionForward perform(ActionMapping mapping,

ActionForm form,

HttpServletRequest request,

HttpServletResponse response)

throws IOException, ServletException;

Большинство проектов используют только версию с "HttpServletRequest".

Целью класса Action является обработка запроса, с помощью его метода perform(), и возвращение объекта ActionForward, который определяет, куда должно быть передано управление (т.е. на какую JSP) , чтобы обеспечить соответствующий запросу ответ. В паттерне проектирования MVC/Model 2 типичный класс Action будет реализовывать в своем методе perform() следующую логику:

Проверять текущее состояние пользовательской сессии (например, проверка того, что пользователь успешно вошел в систему). Если класс Action обнаружит, что пользователь не вошел в систему, то запрос может быть перенаправлен на JSP-страницу, которая просит ввести имя пользователя и пароль для входа в систему. Такая ситуация может возникнуть, если пользователь пытается войти в приложение “в середине” (например, через закладку), или потому что сессия закончилась, и сервлет-контейнер создает новый экземпляр сессии.

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

Выполнить обработку, требуемую для этого запроса (например, сохранить строку в базу данных). Это может быть сделано с помощью логики, непосредственно реализованной внутри Action, но обычно выполняется путем вызова соответствующего вызова Bean бизнес-логики.

Обновить объекты на стороне сервера, котоыре будут использоваться для создания следующей страницы пользовательского интерфейса (обычно beans в контексте запроса или сессии, в зависимости от того, как долго вы желаете хранить эти объекты) .

Вернуть соответствующий объект ActionForward, который определяет JSP-страницу, которая будет использована для генерации этого ответа, основываясь на обновленных beans. Обычно, вы получаете ссылку на такой объект путем вызова findForward() либо из объекта ActionMapping, который вы получили (в случае, если используется логическое имя, локальное для данного mapping), либо из сервлет-контроллера (если вы используете логическое имя, глобальное для всего приложения).

Важные вещи, о которых нужно помнить во время кодирования Action-классов, включают следующее:

Сервлет-контроллер создает только один экземпляр вашего Action-класса, и использует ее для всех запросов. Таким образом, вы должны кодировать ваш Action-класс так, чтобы он корректно работал в многопоточной среде, также как вы должны кодировать метод сервлета service().

Наиболее важным принципом, который следует соблюдать для получения потоко-безопасного (thread-safe) кода, является использование только локальных переменных, а не переменных экземпляра класса, в ваших Action-классах. Локальные переменные создаются в стеке, который присваиваются (вашей JVM) каждому потоку, обслуживающему запрос, поэтому можно не беспокоится об их совместном использовании.

Beans, которые представляют Model вашей системы, могут возбуждать исключения из-за проблем доступа у БД или к другим ресурсам. Необходимо отлавливать все такие исключения в бизнес-логике внутри вашего метода perform(), и протоколировать их в log (протоколе) приложения (вместе с соответствующим stack trace) вызывая servlet.log("Error message text", exception);

В качестве общей рекомендации, можно отметить, что выделение дефицитного ресурса и удержание его в течение нескольких запросов одного и то же пользователя (в пользовательской сессии) может привести к проблемам с масштабируемостью. Старайтесь освобождать ресурсы (такие, как соединения с базой данных) прежде чем передавать управление к соответствующему компоненту VIEW – даже если метод bean который вы должны вызвать, возбуждает исключение.

Кроме того, вы должны стараться избегать слишком больших классов Action. Проще всего получить такие классы это встраивать функциональную логику прямо в Action-классы, а не кодировать их отдельных bean'ах бизнес-логики. Помимо того, что такой подход делает Action-классы трудными для восприятия и поддержки, он также усложняет повторное использование кода бизнес-логики, так как он внедрен внутрь компонента (т.е. Action-класса), который привязан к web-среде выполнения.

Action класс может быть разложен на несколько локальных методов, так чтобы все необходимые свойства передавались через сигнатуры методов. JVM обрабатывает такие свойства с использованием стека, и поэтому они являются thread-safe.

Пример приложения в поставке Struts не следует этому принципу, так как бизнес-логика реализована внутри Action-классов. Это должно рассматриваться как ошибка в дизайне примера, а не свойство, присущее архитектуре Struts, или как образец для подражания.

4.3 Реализация ActionMapping

Для того чтобы успешно работать, контроллеру Struts необходимо знать о том, как каждый запрашиваемый URI должен быть отображен на соответствующие Action-классы. Требуемые знания были инкапсулированы в Java-интерфейсе под названием ActionMapping, наиболее важные свойства которого следующие:

type – полное имя Java-класса, реализующего Action для данного mapping

name – имя form bean, которое будет использоваться данным Action, причем это логическое имя bean, которое определено в конфигурационном файле

pathURI запроса, который сопоставлен данному mapping. Ниже приведены примеры того, как работает сопоставление.

unknown – булево свойство. Устанавливайте его в true, если action для данного mapping является точкой входа в приложение по умолчанию, и должен обрабатывать все запросы, которые не обработаны другим action. Только один Action может быть объявлен как точка входа по умолчанию внутри одного приложения.

validate – установите это булево свойство в true, если нужно вызывать метод validate() в action, ассоциированным с данным mapping.

forwardURI запроса, на который должно быть передано управление, когда вызывается его mapping. Это альтернатива объявлению свойства type.

4.4 Конфигурационный файл Action Mappings

Как сервлет-контроллер узнает о том, какие mappings вы определили? Возможно (но скучно), написать маленький Java-класс, который просто создаст экземпляры нужных нам ActionMapping, и вызовет все необходимые setter-методы. Чтобы упростить эту задачу, в Struts включен модуль Digester, который может прочитать XML-описание желаемых mappings и создать соответствующие объекты. Взгляните на API documentation чтобы ближе познакомиться с Digester.

Задача разработчика – создать XML-файл с именем struts-config.xml и поместить его в каталог WEB-INF своего приложения. Формат этого документа ограничен его определением в "struts-config_1_0.dtd". Корневым элементом в этом файле должен быть <struts-config>.

Внутри элемента <struts-config> находятся 2 важных элемента, которые используются для описания ваших action:

<form-beans>

Эта секция содержит определения ваших form beans. Используйте элемент <form-bean> для определения каждого bean, который имеет следующие важные атрибуты:

name: уникальный идентификатор для данного bean, который будет использоваться для ссылки на него в соответствующих action mappings. Это также имя атрибута сессии или request, под которым данный bean будет сохраняться.

type: полное имя Java-класса для вашего form bean.

<action-mappings>

Эта секция содержит определения ваших action. Используйте элемент <action> для каждого из определяемых action's. Каждый элемент <action> требует определения следующих атрибутов:

path: относительный путь внутри приложения (application context-relative path)для данного action

type: полное имя Java-класса для вашего Action

name: имя элемента <form-bean>, который будет использоваться вместе с этим action

Файл struts-config.xml из примера приложения включает следующую запись о mapping для функции “входа в приложение”, которое используется для иллюстрации приведенных требований. Обратите внимание, что записи для всех остальных actions опущены.

<struts-config>

<form-beans>

<form-bean

name="logonForm"

type="org.apache.struts.example.LogonForm" />

</form-beans>

<global-forwards

type="org.apache.struts.action.ActionForward" />

<forward name="logon" path="/logon.jsp"

redirect="false" />

</global-forwards>

<action-mappings>

<action

path="/logon"

type="org.apache.struts.example.LogonAction"

name="logonForm"

scope="request"

input="/logon.jsp"

unknown="false"

validate="true" />

</action-mappings>

</struts-config>

First the form bean is defined. A basic bean of class "org.apache.struts.example.LogonForm" is mapped to the logical name "logonForm". This name is used as a session or request attribute name for the form bean.

The "global-forwards" section is used to create logical name mappings for commonly used jsp pages. Each of these forwards is available through a call to your action mapping instance, i.e. actionMappingInstace.findForward("logicalName").

As you can see, this mapping matches the path /logon (actually, because the example application uses extension mapping, the request URI you specify in a JSP page would end in /logon.do). When a request that matches this path is received, an instance of the LogonAction class will be created (the first time only) and used. The controller servlet will look for a session scoped bean under key logonForm, creating and saving a bean of the specified class if needed.

Optional but very useful are the local "forward" elements. In the example application, many actions include a local "success" and/or "failure" forward as part of an Action mapping.

<!-- Edit mail subscription -->

<action path="/editSubscription"

type="org.apache.struts.example.EditSubscriptionAction"

name="subscriptionForm"

scope="request"

validate="false">

<forward name="failure" path="/mainMenu.jsp"/>

<forward name="success" path="/subscription.jsp"/>

</action>

Using just these two extra properties, the Action classes in the example application are almost totally independent of the actual names of the JSP pages that are used by the page designers. The pages can be renamed (for example) during a redesign, with negligible impact on the Action classes themselves. If the names of the "next" JSP pages were hard coded into the Action classes, all of these classes would also need to be modified. Of course, you can define whatever local forward properties makes sense for your own application.

One more section of good use is the <data-sources> section, which specifies data sources that your application can use.This is how you would specify a basic data source for your application inside of struts-config.xml:

<struts-config>

<data-sources>

<data-source

autoCommit="false"

description="Example Data Source Description"

driverClass="org.postgresql.Driver"

maxCount="4"

minCount="2"

password="mypassword"

url="jdbc:postgresql://localhost/mydatabase"

user="myusername"/>

</data-sources>

</struts-config>

For information on how to retrieve the data source, see the Accessing Relational Databases section.

4.5 The Web Application Deployment Descriptor

The final step in setting up the application is to configure the application deployment descriptor (stored in file WEB-INF/web.xml) to include all the Struts components that are required. Using the deployment descriptor for the example application as a guide, we see that the following entries need to be created or modified.

4.5.1 Configure the Action Servlet Instance

Add an entry defining the action servlet itself, along with the appropriate initialization parameters. Such an entry might look like this:

<servlet>

<servlet-name>action</servlet-name>

<servlet-class>

org.apache.struts.action.ActionServlet

</servlet-class>

<init-param>

<param-name>application</param-name>

<param-value>

org.apache.struts.example.ApplicationResources

</param-value>

</init-param>

<init-param>

<param-name>config</param-name>

<param-value>

/WEB-INF/struts-config.xml

</param-value>

</init-param>

<init-param>

<param-name>debug</param-name>

<param-value>2</param-value>

</init-param>

<init-param>

<param-name>mapping</param-name>

<param-value>

org.apache.struts.example.ApplicationMapping

</param-value>

</init-param>

<load-on-startup>2</load-on-startup>

</servlet>

The initialization parameters supported by the controller servlet are described below. (You can also find these details in the Javadocs for the ActionServlet class.) Square brackets describe the default values that are assumed if you do not provide a value for that initialization parameter.

application - Java class name of the application resources bundle base class. [NONE]

bufferSize - The size of the input buffer used when processing file uploads. [4096]

config - Context-relative path to the XML resource containing our configuration information. [/WEB-INF/struts-config.xml]

content - Default content type and character encoding to be set on each response; may be overridden by a forwarded-to servlet or JSP page. [text/html]

debug - The debugging detail level for this servlet, which controls how much information is logged. [0]

detail - The debugging detail level for the Digester we utilize in initMapping(), which logs to System.out instead of the servlet log. [0]

factory - The Java class name of the MessageResourcesFactory used to create the application MessageResources object. [org.apache.struts.util.PropertyMessageResourcesFactory]

formBean - The Java class name of the ActionFormBean implementation to use [org.apache.struts.action.ActionFormBean].

forward - The Java class name of the ActionForward implementation to use [org.apache.struts.action.ActionForward]. Two convenient classes you may wish to use are:

org.apache.struts.action.ForwardingActionForward - Subclass of org.apache.struts.action.ActionForward that defaults the redirect property to false (same as the ActionForward default value).

org.apache.struts.action.RedirectingActionForward - Subclass of org.apache.struts.action.ActionForward that defaults the redirect property to true.

locale - If set to true, and there is a user session, identify and store an appropriate java.util.Locale object (under the standard key identified by Action.LOCALE_KEY) in the user's session if there is not a Locale object there already. [true]

mapping - The Java class name of the ActionMapping implementation to use [org.apache.struts.action.ActionMapping]. Two convenient classes you may wish to use are:

org.apache.struts.action.RequestActionMapping - Subclass of org.apache.struts.action.ActionMapping that defaults the scope property to "request".

org.apache.struts.action.SessionActionMapping - Subclass of org.apache.struts.action.ActionMapping that defaults the scope property to "session". (Same as the ActionMapping default value).

maxFileSize - The maximum size (in bytes) of a file to be accepted as a file upload. Can be expressed as a number followed by a "K" "M", or "G", which are interpreted to mean kilobytes, megabytes, or gigabytes, respectively. [250M]

multipartClass - The fully qualified name of the MultipartRequestHandler implementation class to be used for processing file uploads. [org.apache.struts.upload.DiskMultipartRequestHandler]

nocache - If set to true, add HTTP headers to every response intended to defeat browser caching of any response we generate or forward to. [false]

null - If set to true, set our application resources to return null if an unknown message key is used. Otherwise, an error message including the offending message key will be returned. [true]

tempDir - The temporary working directory to use when processing file uploads. [The working directory provided to this web application as a servlet context attribute]

validate - Are we using the new configuration file format? [true]

validating - Should we use a validating XML parse to process the configuration file (strongly recommended)? [true]

4.5.2 Configure the Action Servlet Mapping

Note: The material in this section is not specific to Struts. The configuration of servlet mappings is defined in the Java Servlet Specification. This section describes the most common means of configuring a Struts application.

There are two common approaches to defining the URLs that will be processed by the controller servlet -- prefix matching and extension matching. An appropriate mapping entry for each approach will be described below.

Prefix matching means that you want all URLs that start (after the context path part) with a particular value to be passed to this servlet. Such an entry might look like this:

<servlet-mapping>

<servlet-name>action</servlet-name>

<url-pattern>/execute/*</url-pattern>

</servlet-mapping>

which means that a request URI to match the /logon path described earlier might look like this:

http://www.mycompany.com/myapplication/execute/logon

where /myapplication is the context path under which your application is deployed.

Extension mapping, on the other hand, matches request URIs to the action servlet based on the fact that the URI ends with a period followed by a defined set of characters. For example, the JSP processing servlet is mapped to the *.jsp pattern so that it is called to process every JSP page that is requested. To use the *.do extension (which implies "do something"), the mapping entry would look like this:

<servlet-mapping>

<servlet-name>action</servlet-name>

<url-pattern>*.do</url-pattern>

</servlet-mapping>

and a request URI to match the /logon path described earlier might look like this:

http://www.mycompany.com/myapplication/logon.do

4.5.3 Configure the Struts Tag Library

Next, you must add an entry defining the Struts tag library. There are currently four taglibs that Struts is packaged with.

The struts-bean taglib contains tags useful in accessing beans and their properties, as well as defining new beans (based on these accesses) that are accessible to the remainder of the page via scripting variables and page scope attributes. Convenient mechanisms to create new beans based on the value of request cookies, headers, and parameters are also provided.

The struts-html taglib contains tags used to create struts input forms, as well as other tags generally useful in the creation of HTML-based user interfaces.

The struts-logic taglib contains tags that are useful in managing conditional generation of output text, looping over object collections for repetitive generation of output text, and application flow management.

The struts-template taglib contains tags that define a template mechanism.

Below is how you would define all taglibs for use within your application, in reality you would only specify the taglib's that your application will use:

<taglib>

<taglib-uri>

/WEB-INF/struts-bean.tld

</taglib-uri>

<taglib-location>

/WEB-INF/struts-bean.tld

</taglib-location>

</taglib>

<taglib>

<taglib-uri>

/WEB-INF/struts-html.tld

</taglib-uri>

<taglib-location>

/WEB-INF/struts-html.tld

</taglib-location>

</taglib>

<taglib>

<taglib-uri>

/WEB-INF/struts-logic.tld

</taglib-uri>

<taglib-location>

/WEB-INF/struts-logic.tld

</taglib-location>

</taglib>

<taglib>

<taglib-uri>

/WEB-INF/struts-template.tld

</taglib-uri>

<taglib-location>

/WEB-INF/struts-template.tld

</taglib-location>

</taglib>

This tells the JSP system where to find the tag library descriptor for this library (in your application's WEB-INF directory, instead of out on the Internet somewhere).

4.5.4 Add Struts Components To Your Application

To use Struts, you must copy the .tld files that you require into your WEB-INF directory, and copy struts.jar (and all of the commons-*.jar files) into your WEB-INF/lib directory.

Обновлено: 13.03.2015