суббота, 13 апреля 2013 г.

шаблон страницы PrimeFaces

В корневой тэг нужно добавить пространства имён:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:p="http://primefaces.org/ui"
      xmlns:c="http://java.sun.com/jsp/jstl/core">

из которых первые три - это добавление фэйслетов JSF,
четвёртый - добавление интерфейсов PrimeFaces,
пятый - добавление библиотеки тегов JSTL.

В тег <body> добавляем лайоты:
            <p:layout fullPage="true">
                <p:layoutUnit position="north" size="100" resizable="true" closable="true" collapsible="true">
                    <h1>Заголовок</h1>
                </p:layoutUnit>

                <p:layoutUnit position="south" size="100" closable="true" collapsible="true">
                    фирма inc
                </p:layoutUnit>

                <p:layoutUnit position="west" size="175" header="Меню" collapsible="true">
                    <p:menu>
                        <p:submenu label="Ресурсы">
                            <p:menuitem value="Стартовая" url="index.xhtml"/>
                        </p:submenu>
                    </p:menu>
                </p:layoutUnit>

                <p:layoutUnit position="center">
                    <h3>Центральная область сайта</h3>
                </p:layoutUnit>

            </p:layout>

вторник, 9 апреля 2013 г.

создание JavaEE 6-приложения в netbeans с использованием maven


maven - как система сборки
primeFaces - слой визуализации
netBeans IDE - инструмент разработки

Статья основана на англоязычном видео, доступном здесь. Отличием от него будет использование СУБД Postgre вместо поставляемого вместе с netbeans Derby.

Результат нашей работы - отображение в браузере данных, хранящихся в базе данных.

Стартуем

Создаём новое maven web приложение
Файл > Создать проект > Maven > Вэб приложение
Имя проекта = planner
Идентификатор группы = examples.maven
Версия = 1.0-SNAPSHOT
Пакет = examples.maven.planner
Далее >
Сервер = GlassFish Server 3.1.2
Версия Java EE = Jave EE 6 Web
Готово

В течение минуты проект будет создан.
Мы получили maven-приложение, в чём мы можем убедиться, взглянув на pom.xml, находящийся в узле Файлы проекта во вкладке Проекты

Во всех maven-проектах POM-файл является главным и определяет структуру проекта. В только что созданном приложении pom.xml задаёт только одну зависимость - javaee-web-api 6.0

Также в узле Веб-страницы для нас создана страница index.jsp, которую немедленно надо удалить, поскольку мы будем пользоваться не JSP, а Facelet-ами. Для их добавления в проект (а именно, мы добавляем поддержку PrimeFaces) открываем окно Свойства проекта, выбираем вкладку Платформы, нажимаем кнопку Добавить, в появившемся списке выбираем Приложение JavaServer Faces. Нажимаем в первом окне Ок, затем в большом окне в нижней правой половине Идём во вкладку Компоненты и ставим галочку в чекбоксе напротив PrimeFaces. Нажимаем ещё раз Ок, после чего netBeans несколько секунд думает, но всё заканчивается тем, что нашему проекту становится доступна эта технология. Это подтверждают появившиеся два файла index.xhtml и welcomePrimeFaces.xhtml в узле Веб-страницы. Также в pom.xml появляется зависимость primefaces. Ну и кроме того в web.xml теперь зарегистрирован сервлет PrimeFaces и стартовой страницей объявлена index.xhtml. В свою очередь index.xhtml ссылается на welcomePrimeFaces.xhtml, использующий библиотеку компонентов PrimeFaces.

Теперь мы можем развернут наше приложение. Для этого кликаем правой кнопкой на проекте, выбираем пункт Выполнение, ждём порядка минуты пока в консоли не выскочит надпись BUILD SUCCESS, после чего должна стартовать консоль GlassFish Server, в которой порядка минуты будут идти процессы, по окончании которых в браузере инициативно откроется новая вкладка, в которой будет ссылка, по которой можно кликнуть и попасть в первое маленькое приложение PrimeFaces с хедером, футером, центральной областью и меню слева.

Любителям русских букв нужно ещё обеспечить корректную передачу кириллицы при сабмите формы. Для этого в соответствии с инструкцией отсюда нужно создать класс, имплементирующий интерфейс javax.servlet.Filter и задекларировать его в web.xml.
Класс:
package ru.andrew.myapp;

import java.io.IOException;
import java.io.Serializable;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class CharacterEncodingFilter implements Filter, Serializable {

    private static final long serialVersionUID = -4246457499875267088L;

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        chain.doFilter(req, resp);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void destroy() {
    }
}


Декларация в web.xml:
    <filter>
        <filter-name>Character Encoding Filter</filter-name>
        <filter-class>ru.andrew.myapp.CharacterEncodingFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>Character Encoding Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
После копирования и вставки этих кусков кода не забудьте в java-классе и в xml-коде вместо ru.andrew.myapp написать имя своего пакета.


На этом работа по созданию проекта приложения заканчивается и начинается его кастомизация.

Создадим в своём экземпляре Postgre новую базу данных j2ee:

-- Database: j2ee
-- DROP DATABASE j2ee;
CREATE DATABASE j2ee
  WITH OWNER = postgres
       ENCODING = 'UTF8'
       TABLESPACE = pg_default
       LC_COLLATE = 'Russian_Russia.1251'
       LC_CTYPE = 'Russian_Russia.1251'
       CONNECTION LIMIT = -1;
В этой базе данных появится дефолтная схема public. В этой схеме создадим новую таблицу task:

-- Table: task
-- DROP TABLE task;
CREATE TABLE task
(
  id serial NOT NULL,
  description character varying(250),
  regdate timestamp without time zone DEFAULT ('now'::text)::date,
  enddate timestamp without time zone,
  performed boolean DEFAULT false,
  CONSTRAINT task_pkey PRIMARY KEY (id)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE task
  OWNER TO postgres;
Вставим в таблицу данные:

INSERT INTO task(
            description)
    VALUES ('написать ТЗ по штрих-кодам');

В netbeans включены специальные кодогенераторы для генерации классов сущностей JPA. Netbeans создает классы сущности Java Persistence API, основанные на существующей реляционной базе данных. Классы сущностей используются для представления объектов, чей срок жизни дольше типичного времени выполнения приложения. Данный шаблон создает класс сущности для каждой выбранной таблицы, подставляет аннотации именованных запросов, полей представляющих столбцы, и отношения представляющие внешние ключи.
 Кликаем правой кнопкой на узле проекта Новый > Классы сущностей из базы данных,
В источнике данных выбираем:
Новый источник данных
Имя JNDI = jdbc/postgre
Подключение к базе данных = Новое соединение с базой данных
Драйвер = PostgreSQL
Файл(ы) драйвера = <путь к postgresql.jdbc.jar>
Далее >
Адрес = localhost
Порт = 5432
База данных = j2ee
Имя пользователя = user
Пароль = password
ставим галку в поле Запомнить пароль
по итогам должен получиться JDBC URL = jdbc:postgresql://localhost:5432/j2ee
Далее >
Выберите схему = public

По окончании определения нового источника данных появится список доступных в источнике таблиц, в котором мы выбираем таблицу task. Приятным бантиком от netbeans является также то, что если мы выберем таблицу, которая зависит, например, от каких-то справочников через внешние ключи, то все эти таблицы будут вычислены netbeans-ом и также добавлены в список генерируемых классов. Все остальные экраны проходим без внесения изменений. По окончании работы мастера у нас появляется первый java-класс Task.java, сгенерированный средствами netbeans.

Каждый такой класс имеет аннотацию @Entity, которая делает его сущностным классом JPA.
Аннотация @Table отвечает за маппинг, т.е. соответствие той таблице, записям которой будут соответствовать экземпляры данного класса.
Именованные запросы (Named Queries) дают вам лёгкий доступ к данным без необходимости запоминания растянутых строк запросов.
Также в классе переопределены методы hashCode(), equals() и toString(), которые уже более адекватно чем дефолтные отражают ситуацию.

Также по окончании работы мастера в соответствии со спецификацией JPA в узле Другие исходные файлы > examples.maven.planner > META-INF был создан файл persistence.xml

Давайте быстренько создадим сервлет, чтобы проверить, что подключение к базе данных работает корректно.
Кликаем правой кнопкой на узле Пакеты исходных кодов > Новый > Сервлет
Имя класса = VerifyJPAServlet
Пакет = servlet
Далее >
Вместо того, чтобы регистрировать сервлет в web.xml, мы дадим возможность IDE создать нужную аннотацию в классе сервлета. Поэтому на следующем экране мы ничего не меняем, не ставим никаких галок, и спокойненько завершаем мастер.

Открыв код сервлета можно увидеть ту самую аннотацию, замещающую регистрацию сервлета в web.xml:

@WebServlet(name = "VerifyJPAServlet", urlPatterns = {"/VerifyJPAServlet"})


Сейчас мы можем задеплоить сервлет и посмотреть на его работоспособность:
кликаем правой кнопкой на файле > Выполнить файл (F6), ждём секунду пока сервлет задеплоится на сервере приложеий, и в конце видим окно браузера с приветствием и с именем развёрнутого сервлета.

Теперь вставим в сервлет получение данных из БД и отображение их на веб-странице. Будем использовать удобный кодогенератор, чтобы вставить файл persistence.xml в сервлет. В открытом файле сервлета нажимаем горячие клавиши Alt-Insert, в появившемся меню выбираем Использовать диспетчер сущностей, в результате чего в коде с помощью аннотаций будет объявлена пара переменных, из которых нам важен экземпляр EntityManager:

    @PersistenceContext(unitName = "examples.maven_planner_war_1.0-SNAPSHOTPU")
    private EntityManager em;
имя, заданное в unitName, мы можем увидеть в качестве атрибута Имя единицы персистентности в файле persistence.xml.

Кроме того в коде появился метод сохранения объекта в базе:
    public void persist(Object object) {
        try {
            utx.begin();
            em.persist(object);
            utx.commit();
        } catch (Exception e) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "exception caught", e);
            throw new RuntimeException(e);
        }
    }
который задаёт образец обращения с данными в БД. Сам метод можно закомментировать, а вместо него нужно написать публичный метод, возвращающий экземпляр EntityManager:
    public EntityManager getEntityManager(){
        return em;
    }

В метод processRequest сервлета добавим код:

            out.println(getEntityManager().createNamedQuery("Task.findAll").getResultList());
А в бине Task поменяем метод toString на следующий:
    public String toString() {
        return description + " / " + regdate + "; ";
    }
После этого можно обновить браузер и увидеть там свои только что созданные задачи.

Теперь мы создадим сессионный бин, который в Java EE 6 очень легковесный и он нужен просто чтобы использовать как контроллер.

Кликаем правой кнопкой на узле Пакет исходных кодов > Новый > Сеансовый компонент:
Мия EJB = TaskSessionBean
Пакет = beans
Готово

Мы получаем POJO-класс с аннотацией @Stateless. В этот класс мы перенесём некоторый код из сервлета. С помощью всё тех же горячих клавиш Alt-Insert вызываем контекстное меню, в котором выбираем Использовать диспетчер сущностей.

С помощью тех же горящих клавиш вызываем меню и выбираем в нём Добавить бизнес-метод:
Имя = getTasks
Тип возвращаемого значения = List<Task>
Ок
В тело метода вставляем строку:

        return em.createNamedQuery("Task.findAll").getResultList();

После чего получаем метод:

    public List<Task> getTasks() {
        return em.createNamedQuery("Task.findAll").getResultList();
    }

Сделаем иньекцию нашего сессионного бина в сервлете. Кликаем любимые горячие клавиши Alt-Insert и выбираем в контекстном меню Вызов компонента EJB,
в появившемся диалогом окне выбираем наш проект, а внутри него наш единственный созданный на данный момент сессионный бин TaskSessionBean. В результате чего в нашем классе появится код:

    @EJB
    private TaskSessionBean taskSessionBean;
Теперь мы используем ссылку на него в сервлете:
            out.println(taskSessionBean.getTasks());
Сохраняем все файлы, обновляем браузер и видим то, что нужно. Это значит, что инъекция сессионного бина прошла успешно. Сессионный бин и JPA работают.

Теперь давайте перейдём к финальному слою - к PrimeFaces - к визуальному GUI на переднем крае этого приложения. Мы хотим внутри фэйслет-файла вызвать сессионный бин, который управляет данными из БД. Идём в welcomoPrimeFaces.xhtml, и меняем содержимоое тега:
                <p:layoutUnit position="center">
                    Welcome to PrimeFaces
                </p:layoutUnit>
Ставим курсор внутри этого тега и нажимаем горячую комбинацию Alt-Insert. Выпадает контекстное меню, в котором мы выбираем Таблица данных JSF. После чего генерируется код таблицы:
                    <f:view>
                        <h:form>
                            <h:dataTable value="#{}" var="item">
                            </h:dataTable>
                        </h:form>
                    </f:view>
Из него мы удалим два внешних тега, а в подготовленный шаблон #{} надо вставить ссылку на наш сессионный бин. Для этого мы можем нажать на горячую комбинацию Ctrl-Пробел, но в сформированном списке мы не увидим нашего бина. Всё потому, что мы не подготовились. В данный момент сессионный бин не может быть иньектирован в слой представления, ведь мы ещё не указали сессионному бину аннотацию @Named.

Поэтому идём в наш бин TaskSessionBean, и после аннотации @Stateless добавляем аннотацию @Named. Сразу после добавления эта аннотация будет подсвечена красным, поэтому нужно будет добавить импорт:
import javax.inject.Named;
Но злобство среды после добавления такого импорта не закончится, после этого она пометит красным имя класса сос словами: "Артефакт CDI найден, однако файл beans.xml отсутствует". Этот файл нужен, чтобы определять опциональные правила вставки. Его мы можем создать средствами IDE. Ставим курсор на подсвеченное красным имя файла, нажимаем пару Alt-Enter, и выбираем пункт Создать файл beans.xml. После чего наша проблема будет решена, а на сам файл можно посмотреть в узле Веб-страницы > WEB-INF.

После того как мы всё это сделали с бином, мы можем использовать его в фэйслетах. Возвращаемся в welcomePrimeFaces.xhtml, идём в шаблон #{}, нажимаем горячую комбинацию Ctrl-Пробел, и выбираем в появившемся списке ссылку на наш бин taskSessionBean. Более того, у этого бина мы можем запросить все задачи, написав #{taskSessionBean.tasks}, и если мы при нажатом контроле подведём мышку к этому tasks, то он загорится ссылкой, по которой можно перейти, а переводит он на метод public List<Task> getTasks().
В результате этих шагов определение таблицы будет выглядеть так:

                    <h:dataTable value="#{taskSessionBean.tasks}" var="item">
                        </h:dataTableas


Теперь давайте создадим столбцы в таблице данных. Весь центральный лайот с определёнными столбцами будет выглядеть так:

                <p:layoutUnit position="center">
                    <h3>Запланированные задачи</h3>
                    <h:dataTable value="#{taskSessionBean.tasks}" var="item">
                        <h:column>${item.description}</h:column>
                        <h:column>${item.regdate}</h:column>
                        <h:column>${item.enddate}</h:column>
                        <h:column>${item.performed}</h:column>
                    </h:dataTable>
                </p:layoutUnit>

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

В отдельном посте мы создадим отдельное CRUD-приложение.


После использования Классы сущностей из базы данных мы можем использовать Страницы JSF из классов сущностей, чтобы создать CRUD-проекты.