Pull to refresh

Создаем портлетное приложение по JSR286, часть первая

Reading time9 min
Views2.6K

Цель

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

План работ

1. Создать проект
2. Создать настройки портлета, вывести их на страницу портала

3. Сохранить настройки, проверять настройки валидатором
4. Создать режим просмотра, зависящий от настроек портлета
5. Генерировать и обрабатывать события в рамках одного портлетного приложения.

Я уже писал раньше о том, как активно развивается спецификация JSR286, как Sun один за одним клепает версии порталов, пичкая их разными вкусностями. После выхода в свободное плавание GlassFish, являющегося прямым родственником Sun Java system Server, стоит ожидать, что свободному сообществу отдадут ветку портала, который, как я писал раньше, сейчас мало чем уступает порталу от IBM. Надо внимательно присмотреться к портлетам и тем бонусам, которые появляются при их использовании. О ложках дёгтя тоже збаыать не будем.

Описание приложения


В портлетном приложении есть два портлета: SearchPortlet и DetailsPortlet. С помощью первого портлета пользователь ищет сотрудников, на втором портлете пользователь просматривает результаты.

схема проекта с кратким описанием

Для того, чтобы добиться похожего результата, создайте проект
File -> New project -> Web -> Web Application с названием HRPortletExample.
В качестве сервера выберите OpenPortal Portlet Container 2.0, зарегистрированный ранее (см. мои предыдущие статьи). Выберите поддержку Portlet Support, создайте портлет с именем класса SearchPortlet.
Не забудьте поместить портлет в указанный на схеме пакет.
И так, у нас есть портлетное приложение и портлет-пустышка. Теперь займемся другими вещами.

Инфраструктура проекта

Класс Person

Создаем класс ru.habr.utils.Person. Этот класс будет отвечать за сотрудника.

package ru.habr.utils;
import java.io.Serializable;
public class Person implements Comparable<Person>, Serializable{
  private int id;
  private String name;
  private String surname;
  private String department;
  private String rank;

  public Person(){
  }
  
  public Person(int id, String… data){
    //TO-DO insert length check to avoid ArrayIndexOutOfBoundsException
    this.id     = id;
    this.name    = data[0];
    this.surname  = data[1];
    this.department = data[2];
    this.rank    = data[3];
  }
  
  @Override
  public String toString(){
    StringBuilder sb = new StringBuilder();
    sb.append(name).append(" ").
      append(surname).append(" ::: ").
      append(department).append(" ::: ").
      append(rank);
    return sb.toString();
  }
  
  public String getFullString(){
    StringBuilder sb= new StringBuilder();
    sb.append(name).append(surname).append(department).append(rank);
    return sb.toString();
  }
  
  public String getTokenizedRepersentation(){
    StringBuilder sb = new StringBuilder();
    sb.append(this.id).append(":");
    sb.append(this.name).append(":");
    sb.append(this.surname).append(":");
    sb.append(this.department).append(":");
    sb.append(this.rank);
    
    return sb.toString();
  }
  
  public static Person restorePersonFromTokenizedRepresentation( String tokenizedRepresentation){
    Person person = null;
    String[] dataStr = tokenizedRepresentation.split(":");
    if(dataStr.length==5){
      person = new Person();
      person.setId(Integer.valueOf(dataStr[0]));
      person.setName(dataStr[1]);
      person.setSurname(dataStr[2]);
      person.setDepartment(dataStr[3]);
      person.setRank(dataStr[4]);
    }
    return person;
  }
  
  @Override
  public int hashCode(){
    return department.hashCode();
  }
  
  @Override
  public boolean equals(Object obj){
    boolean isEqual = false;
    
    if(obj instanceof Person){
      if( this.getId() == ((Person)obj).getId() ){
        isEqual = true;
      }
    }
    
    return isEqual;
  }
    
public int compareTo(Person inputPerson) {
    return surname.compareTo(inputPerson.getSurname());
  }
 //...
}

Вместо //... вставляем геттеры и сеттеры для полей класса.
Метод public String getFullString() понадобится для поиска сотрудников, а метод public String getTokenizedRepersentation() и public static Person restorePersonFromTokenizedRepresentation( String tokenizedRepresentation) понадобится для удобного хранения экземпляра класса в виде строки. Для чего нужен такой экстравагантный способ, я объясню ниже. Если уважаемые читатели подскажут, как от него избавиться, я буду безмерно благодарен.
Обратите внимание, что класс описывает методы интерфейса Comparable (чтобы сотрудников можно было сортировать), и помечен интерфейсом — маркером Serializable, для того, чтобы сотрудник мог сериализоваться.
Сортировка пригодится для вывода списка сотрудников на странице, а сериализация нужна для передачи найденного сотрудника от одного портлета другому через события.

Класс UtilClass

Этот класс читает файл с сотрудниками, создает экземпляры класса Person и засовывает и в Map, где ключ — id сотрудника, а значение — сам сотрудник.
Детали вы рассмотрите в исходниках, покажу только метод, осуществляющий поиск:
public ArrayList<Person> searchForPerson(String searchString){
    ArrayList<Person> resultList = null;
    if(searchString == null || searchString.trim().replace(" ", "").length()<1){
      return null;
    }else{
    resultList = new ArrayList<Person>();
    searchString = searchString.trim().replace(" ", "");
      for(Person p: mapWithPersons.values()){
        if(p.getFullString().toLowerCase().contains(searchString.toLowerCase())){
          resultList.add(p);
        }
      }
    }
    return resultList;
  }

Все просто, плоско и параллельно — сотрудник вытягивается в строку и далее осуществляется поиск по совпадению части строки сотрудника и символов, введенных пользователем. Короче, суть не в поиске, едем дальше.

Портлет SearchPortlet

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


Добавление настроек в portlet.xml

Настройки портлета прописываются в portlet.xml, давайте его посмотрим и пропишем их:
<portlet>
    <description>SearchPortlet</description>
    <portlet-name>SearchPortlet</portlet-name>
    <display-name>SearchPortlet</display-name>
    <portlet-class>ru.habr.portlet.SearchPortlet</portlet-class>
    <expiration-cache>0</expiration-cache>
    <supports>
      <mime-type>text/html</mime-type>
      <portlet-mode>VIEW</portlet-mode>
      <portlet-mode>EDIT</portlet-mode>
      <portlet-mode>HELP</portlet-mode>
    </supports>
    <resource-bundle>ru.habr.portlet.messages</resource-bundle>
    <portlet-info>
      <title>SearchPortlet</title>
      <short-title>SearchPortlet</short-title>
    </portlet-info>
        <portlet-preferences>
          <preference>
            <name>showPersonsList</name>
            <value>false</value>
          </preference>
        </portlet-preferences>
  </portlet>

В секции <portlet-preferences> указываются имена настроек (чтобы к ним можно было обращаться) и значения по умолчанию. И то и другое хранится в виде строки, соответственно неизбежны ошибки ввода. Для проверки корректности устанавливаемых настроек применяются валидаторы. О них мы поговорим в следующий раз.

Редактирование метода doEdit в портлете SearchPortlet

Переходим в класс SearchPortlet, ищем метод doEdit, отвечающий отображение режима настройки портлета:
@Override
  public void doEdit(RenderRequest request,RenderResponse response) throws PortletException,IOException {
    //transfer preference value to jsp
    String prefValue = request.getPreferences().getValue(PREF_SHOWPERSONS,"");
    request.setAttribute(PREF_SHOWPERSONS, prefValue);
    
    response.setContentType(«text/html»);    
    PortletRequestDispatcher dispatcher =
    getPortletContext().getRequestDispatcher("/WEB-INF/jsp/SearchPortlet_edit.jsp");
    dispatcher.include(request, response);
  }

К текущим настройкам портлета можно обратиться через request.getPreferences().getValue(PREF_SHOWPERSONS,"")
PREF_SHOWPERSONS — статическое поле класса, хранящее имя настройки showPersonsList.
Если внимательно почитать , то видно, что настройки рекомендуется передавать на jsp в виде атрибутов, хотя на самой jsp можно точно так же обратиться к настройкам через renderRequest.getPreferences().getValue(PREF_SHOWPERSONS,"") Зачем это делать — непонятно.

SearchPortlet_edit.jsp

Выглядит так:
<%@page contentType=«text/html»%>
<%@page pageEncoding=«UTF-8»%>
<%@page import=«javax.portlet.*»%>
<%@ page import=«javax.portlet.*»%>
<%@ page import=«ru.habr.portlet.SearchPortlet»%>
<%@ taglib uri=«java.sun.com/portlet_2_0» prefix=«portlet»%>
<portlet:defineObjects />

<%boolean showPersonList = Boolean.valueOf((String)renderRequest.getAttribute(SearchPortlet.PREF_SHOWPERSONS));%>
  <H4>Редактирование портлета «Поиск сотрудников».</H4>

  <form method=«post» action="&#60;portlet:actionURL/>">
    Отображать список сотрудников на странице?<br>
    <select name="<%=SearchPortlet.PREF_SHOWPERSONS%>">
      <option value=«true» &#60;%= showPersonList? «SELECTED» : "" %&#62; >Да</option>
      <option value=«false» &#60;%= !showPersonList? «SELECTED» : "" %&#62; >Нет</option>
    </select>
    <input type=«submit» value=«Сохранить»>
</form>

Вытаскиваем значение атрибута, отображаем значение, ждем нажатия на кнопку. Сам режим редактирования выглядит так:
схема проекта с кратким описанием

URL для формы генерируется на этапе компиляции jsp при помощи тэга actionURL.

Как сохранить новые настройки и что происходит при нажатии на кнопку «Сохранить» я расскажу в следующей серии!

* This source code was highlighted with Source Code Highlighter.
Tags:
Hubs:
Total votes 6: ↑6 and ↓0+6
Comments5

Articles