отчеты по лабораторным работам / лабораторная работа 7 (jsf) / JavaServer Faces
.pdfjavax.faces.Float javax.faces.convert.FloatConverter
Таким образом, если поле формы связано со свойством типа int или Integer, то конвертация происходит автоматически. В листинге 19 показан компонент, использующийся в нашем демонстрационном приложении для управления контактами. Он связан непосредственно со свойством age c помощью выражения #{contactController.contact.age}:
Листинг 19. Связывание со свойством age: конвертация выполняется автоматически
<%-- age --%>
<h:outputLabel value="Age" for="age" accesskey="age" />
<h:inputText id="age" size="3" value="#{contactController.contact.age}"> </h:inputText>
JSF предоставляет конвертеры для всех примитивных типов, классов-оболочек над примитивными типами, строк и перечислений. Неплохо, не правда ли? Кроме того, есть конвертеры для дат и чисел. Поскольку числа могут задаваться в различных форматах, существующий конвертер позволяет указать конкретный формат, используемый пользователем. То же самое справедливо и для дат. Пример использования конвертера для преобразования строки в дату в соответствии с конкретным форматом показан в листинге 20.
Хотя JSF хорошо справляется с конвертацией строк в примитивные (и схожие с ними) типы по умолчанию, при работе с датами необходимо использовать специальный тег <f:convertDateTime/>. Этот тег использует классы из пакетаjava.text и работает с коротким, длинным и специальными форматами дат. В листинге 20 показано, как использовать<f:convertDateTime/> для проверки того, что в поле ―дата рождения‖ находится дата, отформатированная в видеMM/yyyy (2 цифры месяца/4 цифры года). Список всех поддерживаемых форматов можно найти в документации Java API (см. Ресурсы).
Листинг 20. Указание формата даты
<%-- birthDate --%>
<h:outputLabel value="Birth Date" for="birthDate" accesskey="b" /> <h:inputText id="birthDate" value="#{contactController.contact.birthDate}">
<f:convertDateTime pattern="MM/yyyy"/> </h:inputText>
<h:message for="birthDate" errorClass="errorClass" />
В начало
Специализированные конвертеры JSF
Специализированные конвертеры необходимы, если приходится преобразовывать значения полей в объекты типов, специфичных для данного приложения, например:
String в объект типа PhoneNumber (поля PhoneNumber.areaCode, PhoneNumber.prefix, ...)
String в объект типа Name (поля Name.first, Name.last)
String в объект типа ProductCode (поля ProductCode.partNum,ProductCode.rev, ...)
String в объект типа Group
String в объект типа Tags
Для создания специализированного конвертера необходимо следующее:
1.Создать класс, реализующий интерфейс Converter (полное имя javax.faxes.convert.Converter).
2.Реализовать метод getAsObject(), который будет вызываться для преобразования строкового значения поля в объект (например, типа PhoneNumber).
3.Реализовать метод getAsString, который будет вызываться для получения строкового представления объекта (например, типа PhoneNumber).
4.Зарегистрировать конвертер в контексте Faces.
На рисунке 7 показано, как эти действия вписываются в жизненный цикл приложения JSF:
Валидаторы в JSF
Главной целью конвертации и валидации является подготовка данных для обновления объектов модели. Таким образом, к моменту вызова методов, реализующих логику приложения, можно сделать определенные выводы о состоянии модели. Конвертация и валидация позволяют сконцентрироваться на бизнес-логике приложения, а не на утомительных проверках ввода, таких как проверка на null, на длину, на границы массивов и т.д.
Поэтому разумно, что конвертация и валидация выполняются до того, как данные поступают в управляемые bean-объекты на этапе обновления модели. Как вы помните из раздела "Жизненный цикл обработки запросов в приложениях JSF", эти операции выполняются на этапе проверки данных: сначала конвертация, потом валидация.
ВJSF существует четыре варианта того, как может происходить валидация:
С помощью встроенных компонентов
На уровне приложения
С помощью проверочных методов серверных объектов (inline-валидация)
С помощью специализированных компонентов, реализующих интерфейсValidator Ниже мы рассмотрим все эти варианты.
Стандартная валидация
JSF включает в себя три стандартных компонента для валидации:
DoubleRangeValidator: Проверяет, что значение компонента укладывается в интервал, определяемый нижней границей, верхней границей или и тем, и другим. Значение должно быть числом.
LongRangeValidator: Проверяет, что значение укладывается в интервал, определяемый нижней границей, верхней границей или и тем, и другим. Значение должно быть числом, преобразуемым к типу long.
LengthValidator: Проверяет, что длина значения укладывается в интервал, определяемый нижней границей, верхней границей или и тем, и другим. Значение должно быть типа String.
Внашем примере возраст в данных контакта может быть любым числом. Но поскольку такие значения, как –2, не имеют смысла, стоит связать это поле с правилом валидации. В листинге 29 показан пример кода, выполняющего валидацию с
помощью элемента <f:validateLongRange> для обеспечения корректности возрастных данных в модели приложения.
Листинг 29. Валидация возрастных данных с помощью элемента <f:validateLongRange>
<%-- возраст (age) --%>
<h:outputLabel value="Age" for="age" accesskey="age" />
<h:inputText id="age" size="3" value="#{contactController.contact.age}"> <f:validateLongRange minimum="0" maximum="150"/>
</h:inputText>
<h:message for="age" errorClass="errorClass" />
Разобравшись с полем age, можно перейти к ограничениям на длину строки поля FirstName (см. листинг 30).
Листинг 30. Проверка длины строки свойства firstName
<%-- Имя (first name) --%>
<h:outputLabel value="First Name" for="firstName" accesskey="f" /> <h:inputText id="firstName" label="First Name" required="true"
value="#{contactController.contact.firstName}" size="10" > <f:validateLength minimum="2" maximum="25" />
</h:inputText>
<h:message for="firstName" errorClass="errorClass" />
Несмотря на то, что встроенная валидация JSF применима во многих случаях, ее возможности ограничены. Если речь идет о валидации адресов электронной почты, телефонных номеров, URL, дат и т.д., то иногда лучше создавать собственные валидаторы (мы обсудим эту тему далее). Кроме того, можно использовать валидаторы, предоставляемые такими библиотеками, как Tomahawk, Shale, JSF-Validations и Crank (см. Ресурсы).
Валидация уровня приложения
Под валидацией уровня приложения понимается непосредственно бизнес-логика. В JSF она отделена от первичной валидации форм и их полей. Как правило, валидация уровня приложения заключается в добавлении в методы управляемых bean-объектов кода, который использует модель приложения для проверки уже помещенных в нее данных. Например, в случае корзины покупок валидация форм может проверять корректность значения, определяющего количество тех или иных товаров, но для проверки того, что пользователь не превысил свою кредитную линию, необходима бизнес-логика. Это еще один пример принципа разделения ответственности (separation of concerns) в JSF. Допустим, пользователь нажал на кнопку, связанную с action-методом, исполняемом на этапе вызова приложения (см.рисунок 5). Перед тем, как с данными будут произведены какие либо действия (что, как правило, происходит в фазе обновления модели), можно проверить, являются ли введенные данные корректными с точки зрения бизнес-правил приложения.
Например, пусть в нашем приложении пользователь нажал на кнопку Update/Add, связанную с методом контроллераpersist(). В этот метод можно добавить фрагмент кода, проверяющий, существует ли уже такая комбинация полейfirstName/lastName в системе. Если такой контакт уже существует, можно также добавить сообщение в объектFacesContext, а затем указать JSF, что следует сохранить текущее представление, вернув null в качестве исхода (если для данного метода определены правила переходов).
Теперь давайте вернемся к нашему приложению и добавим некоторую логику уровня приложения в метод persist(), как показано в листингах 31 и 32. В листинге 31 показана валидация уровня приложения в контроллере:
Листинг 31. Валидация уровня приложения в классе контроллера
public class ContactController { public String persist() {
/* Валидация уровня приложения. */ try {
contact.validate();
} catch (ContactValidationException contactValidationException) { addErrorMessage(contactValidationException.getLocalizedMessage()); return null;
}
/* Форма доступна, ссылка для добавления - нет. */ form.setRendered(false); addNewCommand.setRendered(true);
/* Добавление сообщения о статусе операцмм. */
if (contactRepository.persist(contact) == null) { addStatusMessage("Added " + contact);
} else {
addStatusMessage("Updated " + contact);
}
return "contactPersisted";
}
private void addErrorMessage(String message) { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(
FacesMessage.SEVERITY_ERROR, message, null));
}
Как видно из листинга 31, метод persist() вызывает метод validate() класса Сontact. Он обрабатывает все исключения и преобразует их в сообщения об ошибках – объекты типа FacesMessage. В случае выдачи исключения онnull, что означает: оставаться на текущем представлении и не переходить к следующему.
Код, выполняющий саму валидацию, находится в модели — в методе validate() класса Contact (см. листинг 32). Благодаря этому становится возможным добавлять новые правила валидации, не затрагивая при этом слои контроллера и представления.
Листинг 32. Валидация в слое модели, а не контроллера
...
public class Contact implements Serializable {
...
public void validate() throws ContactValidationException { if (
(homePhoneNumber == null || ".equals(homePhoneNumber)) & (workPhoneNumber == null || ".equals(workPhoneNumber)) & (mobilePhoneNumber == null || ".equals(mobilePhoneNumber))
) {
throw new ContactValidationException("At least one phone number" + "must be set");
}
}
Валидация уровня приложения проста и легко реализуема. Ее преимущества:
простота реализации;
отсутствие необходимости специального класса (специализированного валидатора);
отсутствие необходимости указывать валидатор при разработке страниц представления; Недостатком валидации уровня приложения является то, что она выполняется после других форм валидации
(стандартной, специализированной и компонентной), поэтому сообщения об ошибках появляются только после того, как вся остальная валидация прошла успешно.
Вцелом валидацию уровня приложения следует использовать только там, где необходима проверка с учетом бизнеслогики.
Автономные специализированные валидаторы
JSF позволяет создавать подключаемые валидирующие компоненты, которые можно использовать в различных Webприложениях.
Для создания валидатора необходимо сделать следующее:
1.класс, реализующий интерфейс Validator (javax.faces.validator.Validator).
2.Реализовать метод validate().
3.Зарегистрировать валидатор в файле faces-config.xml.
4.Использовать тег <f:validator/> на страницах JSP.
Далее мы остановимся на каждом из этих шагов, рассмотрев пример создания специализированного валидатора.
Работы с обработчиками событий жизненного цикла
Согласно документации по JSF API (см. Ресурсы), PhaseListener (обработчик событий жизненного цикла) – это "интерфейс, реализуемый объектами, которым необходимы уведомления о начале или окончании каждой стандартной фазы жизненного цикла обработки запроса" (Sun Microsystems Inc., 2006 г.). Теперь, когда вы познакомились с конвертерами, валидаторами и action-методами, пришло время создать несколько обработчиков событий жизненного цикла. В предыдущих версиях JSF все обработчики событий были глобальными. В JSF 1.2 появилась возможность регистрировать обработчики событий на уровне представления с помощью тега <f:phaseListener binding="..." />.
Создание обработчиков событий жизненного цикла
Для создания обработчика событий жизненного цикла необходимо реализовать интерфейс PhaseListener, как показано в листинге 42:
Листинг 42. DebugPhaseListener
package com.arcmind.phase;
import javax.faces.event.PhaseEvent; import javax.faces.event.PhaseId; import javax.faces.event.PhaseListener;
@SuppressWarnings("serial")
public class DebugPhaseListener implements PhaseListener {
public void beforePhase(PhaseEvent phaseEvent) { System.out.println("------ BEFORE PHASE " + phaseEvent.getPhaseId());
}
public void afterPhase(PhaseEvent phaseEvent) { System.out.println("------ AFTER PHASE " + phaseEvent.getPhaseId());
if (phaseEvent.getPhaseId() == PhaseId.RENDER_RESPONSE) { System.out.println("REQUEST END\n\n");
}
}
public PhaseId getPhaseId() { return PhaseId.ANY_PHASE;
}
}
Классы, реализующие PhaseListener, будут вызываться перед началом входа и после выхода из каждой фазы цикла JSF. Фазы, о которых необходимо получать уведомления, указываются с помощью метода getPhaseId(). КонстантаPhaseId.ANY_PHASE означает, что уведомления будут посылаться для всех фаз.
Класс DebugPhaseListener просто печатает имя события, которое привело к его вызову, чтобы была понятна последовательность действий. Для большей наглядности добавим вызовы System.out.println ко всем методам конвертации, валидации и бизнес-логики. Кроме того, вызовы System.out.println будет полезно добавить в свойство firstName, чтобы видеть, в какие моменты к нему происходят обращения.
Таким образом, используя обработчики событий жизненного цикла, вы можете произвольно менять логику обработки запросов, что позволяет гибко настраивать JSF для нужд вашего приложения.
Expression Language (EL) — скриптовый язык выражений, который позволяет получить доступ к Java компонентам (JavaBeans) из JSP. Начиная с JSP 2.0 используется внутриJSP тегов для отделения Java кода от JSP для обеспечения лѐгкого доступа к Java компонентам.
Развитие EL происходило с целью сделать его более простым для дизайнеров, которые имеют минимальные познания в языке программирования Java. До появления языка выражений, JSP имел несколько специальных тегов таких
как скриптлеты (англ.), выражения и т. п. которые позволяли записывать Java код непосредственно на странице. С использованием языка выражений веб-дизайнер должен знать только то, как организовать вызов соответствующих javaметодов.
Используйте EL всегда, когда надо подставить нестатическое значение. Используйте EL всегда, когда возможно обойтись без Java-вставок. А лучше, никогда не использовать Java-вставок. Например, я никогда не использую Java-вставки и на практике всегда удаѐтся обойтись без них.
Например, так:
<a href="show.jsp?id=${myObject.id}">${myObject.title}</a>
Или в связке с Custom Tags, например, с JSTL:
<ul>
<c:forEach values="${pageBean.myItems}" var="item"> <li>${item.title}</li>
</c:forEach>
</ul>
Или вот, например, для раскраски строк таблицы
<c:forEach values="${myTable.rows}" var="row"> <tr class="${my:rowClass(row)}">..... </tr>
</c:forEach>