angle-left

ICEfaces 2 и JSF 2 в действии

В этой статье я расскажу об использовании Icefaces 2


        После того, как мы подключили ICEfaces 2 (см.  "Введение в ICEfaces 2.0") приступим к работе с самим фреймворком на примере небольшого приложения.

Наше приложение будет отвечать за работу с пользователями: добавление, отображение списка пользователей с координатами их проживания. Данные о пользователях у нас будут храниться на уровне сеанса (sessionScope). Приложение обучающее, довольно простое и показывает часть возможностей работы ICEfaces 2 вместе с JSF 2.

Приступим. 

Для начала создадим template на основе facelets.

Facelets предоставляет возможности по созданию шаблонов, разбивки страниц на блоки, что увеличивает читабельность кода и удобство работы с ним. Facelets появились еще с JSF 1, но не входили тогда в его состав, и их приходилось подключать отдельно, теперь же этого делать не нужно и достаточно только подключить JSF 2.
 

/templates/template.xtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:ice="http://www.icesoft.com/icefaces/component"
      xmlns:f="http://java.sun.com/jsf/core"
      >

    <h:head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title><ui:insert name="title">#{msgs['application.title']}</ui:insert></title>
        <ice:outputStyle href="#{styleBean.themeCssFilePath}"/>
        <ice:outputStyle href="#{styleBean.baseCssPath}style.css"/>
    </h:head>
    <h:body>
        <!-- header -->
        <ice:panelGroup styleClass="header">
            <ice:outputLink value="http://www.tune-it.ru">
                <ice:graphicImage value="#{styleBean.baseImagePath}company_logo.png" styleClass="logo"/>
            </ice:outputLink>
            <ice:panelGroup>
                <ice:outputText value="#{msgs['application.header']}" styleClass="text"/>
            </ice:panelGroup>
        </ice:panelGroup>
        
        <!-- content -->
        <ice:panelGroup>
            <ui:insert name="body">Default BODY</ui:insert>
        </ice:panelGroup>
        
        <!-- footer -->
        <hr/>
        <ice:outputLink value="http://www.tune-it.ru">
            <ice:outputText value="&copy; #{msgs['application.copyright']}" />
        </ice:outputLink>
        <ice:outputFormat style="float: right;" value="#{msgs['application.stage']}">
            <f:param value="#{facesContext.application.projectStage}"/>
        </ice:outputFormat>
    </h:body>
</html>

 

В блок <h:head> .. </h:head> мы включаем заголовки нашей страницы (title, подключение css стилей)

В блок <h:body> .. </h:body> мы включаем тело нашей страницы (header и footer, которые будут отображаться на каждой странице, использующей шаблон, и сам контент)

В нашем шаблоне будет меняться только контент. За это отвечает блок

<ui: insert name="body">Default BODY</ui:insert>

В страницах, использующих этот шаблон, должен быть объявлен блок, который и будет заменять блок нашего шаблона с именем "body". 

<ui:define name="body"> ..</ui:define>

На основе созданного нами template.xhtml создаем главную страничку. Она будет состоять из формы добавления пользователя и таблицы, отображающий список пользователей с местом их проживания. Подключение нужного нам template осуществляется в блоке <ui:composition ..>

/pages/index.xhtml

<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:c="http://java.sun.com/jsp/jstl/core"
                xmlns:ace="http://www.icefaces.org/icefaces/components"
                xmlns:ice="http://www.icesoft.com/icefaces/component"
                template="/templates/template.xhtml"
                >

    <ui:define name="body">

        <ice:panelGroup>

            <ice:panelGrid columns="2" styleClass="wrapper" columnClasses="column1 column-top, column2 column-top">
                <ice:column>
                    <ui:include src="userForm.xhtml" />
                </ice:column>

                <ice:column>
                    <ui:include src="userList.xhtml" />
                </ice:column>
            </ice:panelGrid>
        </ice:panelGroup>

    </ui:define>

</ui:composition>

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

Создадим класс формы. В форме будут поля: имя, фамилия, возраст, адрес.

UserForm.java

package ru.tuneit.example.bean.user;

import java.io.Serializable;

/**
 * @author nicola
 * @version 1.0 (Aug 15, 2011)
 */
public class UserForm implements Serializable {
    
    private static final long serialVersionUID = 1L;

    private String firstName = "";
    
    private String secondName = "";
    
    private Long age = null;
    
    private String address = "";

    public UserForm() {
    }

    public UserForm(String firstName, String secondName, Long age, String address) {
        this.firstName = firstName;
        this.secondName = secondName;
        this.age = age;
        this.address = address;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public Long getAge() {
        return age;
    }

    public void setAge(Long age) {
        this.age = age;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getSecondName() {
        return secondName;
    }

    public void setSecondName(String secondName) {
        this.secondName = secondName;
    }
    
    public void reset() {
        firstName = "";
        secondName = "";
        age = null;
        address = "";
    }  
    
}

 

Теперь создадим класс, отвечающий за пользователя. Он будет нашей моделью.

User.java

package ru.tuneit.example.model;

import java.io.Serializable;
import ru.tuneit.example.bean.user.UserForm;

/**
 * @author nicola
 * @version 1.0 (Jun 20, 2011)
 */
public class User implements Serializable {
    
    private static final long serialVersionUID = 1L;
    
    private String firstName;
    
    private String secondName;
    
    private Long age;
    
    private String address;
    
    private boolean selected;

    public User(UserForm userForm) {
        this(userForm.getFirstName(), userForm.getSecondName(), userForm.getAge(), userForm.getAddress());
    }
    
    public User(String firstName, String secondName, Long age, String address) {
        this.firstName = firstName;
        this.secondName = secondName;
        this.age = age;
        this.address = address;
        this.selected = false;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getSecondName() {
        return secondName;
    }

    public void setSecondName(String secondName) {
        this.secondName = secondName;
    }

    public Long getAge() {
        return age;
    }

    public void setAge(Long age) {
        this.age = age;
    }

    public boolean getSelected() {
        return selected;
    }

    public void setSelected(boolean selected) {
        this.selected = selected;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final User other = (User) obj;
        if ((this.firstName == null) ? (other.firstName != null) : !this.firstName.equals(other.firstName)) {
            return false;
        }
        if ((this.secondName == null) ? (other.secondName != null) : !this.secondName.equals(other.secondName)) {
            return false;
        }
        if (this.age != other.age && (this.age == null || !this.age.equals(other.age))) {
            return false;
        }
        if ((this.address == null) ? (other.address != null) : !this.address.equals(other.address)) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 23 * hash + (this.firstName != null ? this.firstName.hashCode() : 0);
        hash = 23 * hash + (this.secondName != null ? this.secondName.hashCode() : 0);
        hash = 23 * hash + (this.age != null ? this.age.hashCode() : 0);
        hash = 23 * hash + (this.address != null ? this.address.hashCode() : 0);
        return hash;
    }
    
    
}

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

С помощью аннотаций мы регистрируем наш бин на уровне сессии с именем "userBean". Параметр name можно не выставлять, тогда имя будет присвоено автоматически по названию класса бина, но начинаться будет со строчной буквы:

@ManagedBean (name = "userBean") 
@SessionScoped

С помощью аннотации @ManagedProperty мы выставляем свойства нашего бина, которые будут выставлены ему после его создания.

UserBean.java

package ru.tuneit.example.bean.user;

import ru.tuneit.example.model.User;
import com.icesoft.faces.component.ext.RowSelectorEvent;
import com.icesoft.faces.context.effects.Effect;
import com.icesoft.faces.context.effects.Highlight;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.SessionScoped;
import javax.faces.event.ActionEvent;

import org.apache.log4j.Logger;
import ru.tuneit.example.utils.faces.FacesUtils;


@ManagedBean (name = "userBean")
@SessionScoped
public class UserBean implements Serializable {
    
    private static final long serialVersionUID = 1L;
    
    private static final Logger _log = Logger.getLogger(UserBean.class.getName());
    
    private List<User> users;
    
    private UserForm userForm = new UserForm();
    
    @ManagedProperty("190103, г. Санкт-Петербург, 10-я Красноармейская ул., д.22 литера А")
    private String address;
    
    private Effect effect = new Highlight("#C4C4C4");
    
    
    public UserBean() {
    }

    @PostConstruct
    public void initUsers() {
        users = new ArrayList<User>() {{
            add(new User("Иван", "Иванов", 11L, "Санкт-Петербург, Измайловский пр., д2"));
            add(new User("Петр", "Петров", 19L, "Санкт-Петербург, Рижский пр., д3"));
            add(new User("Сергей", "Сергеев", 21L, "Санкт-Петербург, Средний пр., д4"));
            add(new User("Пух", "Винни", 77L, "Санкт-Петербург, Вознесенский пр., д2"));
            add(new User("Пята", "Чок", 74L, "Санкт-Петербург, Ленинский пр., д2"));
        }};
    }
    
    public void selectUser(RowSelectorEvent event) {
        try {
            User userSelected = (User)FacesUtils.getVariable("user");
            for (User u : users) {
                if (!u.equals(userSelected)) {
                    u.setSelected(false);
                }
            }
            address = userSelected.getAddress();
            FacesUtils.refresh();

        } catch (Exception e) {
            _log.error(e, e);
        }
    }
    
    public void save(ActionEvent event) {
        users.add(new User(userForm));
        userForm.reset();
        effect.setFired(false);
    }
    
    public void cancel(ActionEvent event) {
        userForm.reset();
    }
    
    public int getUsersSize() {
        return users != null ? users.size() : 0;
    }

    public UserForm getUserForm() {
        return userForm;
    }

    public void setUserForm(UserForm userForm) {
        this.userForm = userForm;
    }

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public Effect getEffect() {
        return effect;
    }

    public void setEffect(Effect effect) {
        this.effect = effect;
    }
    
}

В нашем бине есть два метода: save(ActionEvent event) и cancel(ActionEvent event). Один отвечает за сохранение пользователя, другой за очистку формы. Они являются ActionListener-ами и висят на соответствующих кнопках <ice:commandButton ..>.

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

/pages/userForm.xhtml

<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:c="http://java.sun.com/jsp/jstl/core"
                xmlns:ace="http://www.icefaces.org/icefaces/components"
                xmlns:ice="http://www.icesoft.com/icefaces/component"
                >

    <ice:form id="addUserForm">
        <ice:panelCollapsible expanded="true">
            <f:facet name="header">
                <ice:outputText value="#{msgs['user.addUser']}" />
            </f:facet>

            <ice:panelGroup effect="#{userBean.effect}">
                <ice:panelGrid columns="2">
                    <ice:column>
                        <ice:outputLabel for="firstName" value="#{msgs['user.firstName']}" />
                    </ice:column>
                    <ice:column>
                        <ice:inputText id="firstName"
                                       value="#{userBean.userForm.firstName}"
                                       required="true"
                                       requiredMessage="#{msgs['user.firstName.requiredMessage']}"
                                       />
                        <ice:message for="firstName"/>
                    </ice:column>

                    <ice:column>
                        <ice:outputLabel for="secondName" value="#{msgs['user.secondName']}" />
                    </ice:column>
                    <ice:column>
                        <ice:inputText id="secondName"
                                       value="#{userBean.userForm.secondName}"
                                       required="true"
                                       requiredMessage="#{msgs['user.secondName.requiredMessage']}"
                                       />
                        <ice:message for="secondName"/>
                    </ice:column>

                    <ice:column>
                        <ice:outputLabel for="age" value="#{msgs['user.age']}" />
                    </ice:column>
                    <ice:column>
                        <ice:inputText id="age"
                                       value="#{userBean.userForm.age}"
                                       validatorMessage="#{msgs['user.age.validateMessage']}"
                                       >
                            <f:validateLongRange minimum="1" maximum="150"/>
                        </ice:inputText>
                        <ice:message for="age"/>
                    </ice:column>

                    <ice:column>
                        <ice:outputLabel for="address" value="#{msgs['user.address']}" />
                    </ice:column>
                    <ice:column>
                        <ice:inputTextarea id="address"
                                           value="#{userBean.userForm.address}"
                                           required="true"
                                           requiredMessage="#{msgs['user.address.requiredMessage']}"
                                           />
                        <ice:message for="address"/>
                    </ice:column>

                    <ice:column />
                    <ice:column>
                        <ice:messages globalOnly="true" layout="table"/>

                        <ice:commandButton value="#{msgs['user.save']}"
                                           actionListener="#{userBean.save}"
                                           />

                        <ice:commandButton value="#{msgs['user.cancel']}"
                                           actionListener="#{userBean.cancel}"
                                           immediate="true"
                                           />
                    </ice:column>
                </ice:panelGrid>
            </ice:panelGroup>

        </ice:panelCollapsible>
    </ice:form>

</ui:composition>

 

Теперь опишем страницу отображающую список наших пользователей с адресом их проживания. Мы используем <ice:dataTable ..> компонент для отображения пользователей c <ice:rowSelector ..>, который отвечает за выделение строки и реакцию на нажатие по строке. При большом количестве пользователей, автоматически производится разбивка таблицы на страницы при помощи <ice:dataPaginator ..>

/pages/userList.xhtml

<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:c="http://java.sun.com/jsp/jstl/core"
                xmlns:ace="http://www.icefaces.org/icefaces/components"
                xmlns:ice="http://www.icesoft.com/icefaces/component"
                >

    <ice:form id="listUsersForm">
        <ice:panelCollapsible expanded="true">
            <f:facet name="header">
                <ice:outputText value="#{msgs['user.usersTable']}" />
            </f:facet>

            <ice:dataTable id="usersTable"
                           value="#{userBean.users}"
                           var="user"
                           columnWidths="40%, 40%, 20%"
                           styleClass="usersTable"
                           rows="5"
                           >
                <ice:column>
                    <ice:rowSelector id="userSelector"
                                     value="#{user.selected}"
                                     multiple="false"
                                     selectionListener="#{userBean.selectUser}"
                                     />

                    <f:facet name="header">
                        <ice:outputText value="#{msgs['user.firstName']}" />
                    </f:facet>
                    <ice:panelGroup>
                        <ice:outputText value="#{user.firstName}"></ice:outputText>
                    </ice:panelGroup>
                </ice:column>

                <ice:column>
                    <f:facet name="header">
                        <ice:outputText value="#{msgs['user.secondName']}" />
                    </f:facet>
                    <ice:panelGroup>
                        <ice:outputText value="#{user.secondName}"></ice:outputText>
                    </ice:panelGroup>
                </ice:column>

                <ice:column>
                    <f:facet name="header">
                        <ice:outputText value="#{msgs['user.age']}" />
                    </f:facet>
                    <ice:panelGroup>
                        <ice:outputText value="#{user.age}"></ice:outputText>
                    </ice:panelGroup>
                </ice:column>

            </ice:dataTable>

            <ice:dataPaginator rendered="#{userBean.usersSize > 5}"
                               for="usersTable"
                               fastStep="3"
                               paginator="true"
                               paginatorMaxPages="4"
                               style="margin-left: auto; margin-right: auto;"
                               >
                <f:facet name="first">
                    <ice:graphicImage url="#{styleBean.themeImageDirectory}arrow-first.gif"
                                      style="border:none;"
                                      title="#{msgs['page.dataScrolling.firstpage']}"
                                      />
                </f:facet>
                <f:facet name="last">
                    <ice:graphicImage url="#{styleBean.themeImageDirectory}arrow-last.gif"
                                      style="border:none;"
                                      title="#{msgs['page.dataScrolling.lastpage']}"
                                      />
                </f:facet>
                <f:facet name="previous">
                    <ice:graphicImage url="#{styleBean.themeImageDirectory}arrow-previous.gif"
                                      style="border:none;"
                                      title="#{msgs['page.dataScrolling.previouspage']}"
                                      />
                </f:facet>
                <f:facet name="next">
                    <ice:graphicImage url="#{styleBean.themeImageDirectory}arrow-next.gif"
                                      style="border:none;"
                                      title="#{msgs['page.dataScrolling.nextpage']}"
                                      />
                </f:facet>
                <f:facet name="fastforward">
                    <ice:graphicImage url="#{styleBean.themeImageDirectory}arrow-ff.gif"
                                      style="border:none;"
                                      title="#{msgs['page.dataScrolling.fastforward']}"
                                      />
                </f:facet>
                <f:facet name="fastrewind">
                    <ice:graphicImage url="#{styleBean.themeImageDirectory}arrow-fr.gif"
                                      style="border:none;"
                                      title="#{msgs['page.dataScrolling.fastbackward']}"
                                      />
                </f:facet>
            </ice:dataPaginator>

            <ice:panelGroup styleClass="map">
                <ice:outputLabel for="map" value="#{msgs['user.map']}" />
                <ice:outputText value="#{userBean.address}" style="clear: left; display: block; font-size: 1.2em;"/>
                <ice:gMap id="map" address="#{userBean.address}" zoomLevel="8">
                    <ice:gMapControl id="largectrl" name="GLargeMapControl" rendered="true"/>
                    <ice:gMapControl id="scalectrl" name="GScaleControl" rendered="true"/>
                    <ice:gMapControl id="typectrl" name="GMapTypeControl" rendered="true"/>
                    <ice:gMapMarker id="gmarker" latitude="59" longitude="30"  rendered="true">
                        <ice:gMapLatLng id="glatlng" latitude="59.544467" longitude="30.174242"/>
                    </ice:gMapMarker>
                </ice:gMap>
            </ice:panelGroup>

        </ice:panelCollapsible>                
    </ice:form>

</ui:composition> 

Для отображения адреса проживания используется компонет <ice:gMap ../>, который отображает Google map карту. 

В итоге у нас должно было получиться что-то подобное:

 

 

Пишите. Спрашивайте. Отвечу.

 

Исходники

  1. Проект Netbeans 7.0
  2. Библиотеки

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

Ресурсы:

1) facelets developer documentation

2) What`s New in JSF 2 ?

3) ICEFaces 2 tutorials

S/N/V/      

tuneit
 

Образование:  СПБГУ ИТМО. Кафедра вычислительной техники.

Интересы: железо, софт, web-приложения (java EE, ICEfaces, LIferay, DotCMS, PHP, CSS, html, JavaScript, CMS Drupal, PostgreSQL, MySql, Oracle...)

Увлечения : активно занимаюсь спортом :  плаванием, джиу-джитсу, волейболом, бегом итд. Люблю отдыхать на природе.

Контакты:

  •   Nicola.russian
  •   Nicola.russian