Java Server Faces to całkiem ciekawy framework dla Javy. O jego zaletach dużo by pisać, toteż odsyłam w tym temacie do odpowiednich wpisów w Sieci. JSF ma jednak jedną wadę; dość słabo wspiera przesyłanie parametrów za pomocą metody GET protokołu HTTP. Oto krótki poradnik jak używać parametrów przekazanych za pomocą metody GET do strony obsługiwanej przez Java Server Faces.

Załóżmy, że mamy listę pracowników, z poziomu której możemy podejrzeć konkretnego zatrudnionego. Do obsługi naszej miniaplikacji wykorzystamy zatem dwie  strony employee_list.jsp i employee.jsp. Przyjmijmy także, że chcielibyśmy, aby dostęp do szczegółów dot. pracownika możliwy był po wprowadzeniu adresu np. http://localhost:8080/company/employee.jsf?e=10. Naszą aplikację zasili ziarno zarządzane (managed bean) CompanyController, którą wyposażamy we własność selectedEmployeeId i własność selectedEmployee zawierającą obiekt wskazanego pracownika:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package net.schowek.jsf.ui;
 
public class CompanyController
{
    @EJB
    CompanyServiceLocal companyService;
 
    private DataModel   empDataModel;
    private Long        selectedEmployeeId;
    private Employee    selectedEmployee;
 
    public DataModel getAllEmployees()
    {
        empDataModel = new ListDataModel();
        empDataModel.setWrappedData( companyService.findAll() );
        return empDataModel;
    }
 
    public Employee getSelectedEmployee()
    {
        if (selectedEmployee == null)
        {
            selectedEmployee = (Employee) companyService.findById( selectedEmployeeId );
        }
        return selectedEmployee;
    }
 
    public void setSelectedEmployee( Employee selectedEmployee )
    {
        this.selectedEmployee = selectedEmployee;
    }
 
    public Long getSelectedEmployeeId()
    {
        return selectedEmployeeId;
    }
 
    public void setSelectedEmployeeId( Long selectedEmployeeId )
    {
        this.selectedEmployeeId = selectedEmployeeId;
    }
}

Wygląda to więc dość standardowo, tylko posługujemy sie tą tajemniczą własnością selectedEmployeeId. I skąd nasz CompanyController ma wiedzieć, że w tejże własności ma znaleźć się parametr przekazany w metodzie GET pod zmienną ‚e‚?

Zdefiniujemy to pliku faces-config.xml:

<managed-bean>
    <managed-bean-name>company</managed-bean-name>
    <managed-bean-class>net.schowek.jsf.ui.CompanyController</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
    <managed-property>
        <property-name>selectedEmployeeId</property-name>
        <value>#{param.e}</value>
    </managed-property>
</managed-bean>

Oraz nasze pliczki stron:

Lista pracowników:

<!-- employee_list.jsp -->
<f:view>
    <h:form>
        <h:datatable var="employee" value="#{company.allEmployees}">
            <h:column>
                <h:outputtext value="#{employee.id}" />
            </h:column>
            <h:column>
                <h:outputtext value="#{employee.name}" />
            </h:column>
            <h:column>
                <h:outputlink value="employee.jsf">
                    <f:verbatim>Szczegóły</f:verbatim>
                    <f:param value="#{employee.id}" name="e" />
                </h:outputlink>
            </h:column>
        </h:datatable>
    </h:form>
</f:view>

…, karta pracownika…

<!-- employee.jsp -->
<f:view>
    <h:form>
        <h2>Szczegóły pracownika #<h:outputText value="#company.selectedEmployee.id"/></h2>
        <div>
            <span>Imię i nazwisko:</span>
            <span><h:outputText value="#company.selectedEmployee.name"/></span>
        </div>
        <div>
            <span>Stanowisko:</span>
            <span><h:outputText value="#company.selectedEmployee.position"/></span>
        </div>
        <div>
            <span>Dodatkowe informacje:</span>
            <div><h:outputText value="#company.selectedEmployee.comments"/></div>
        </div>
    </h:form>
</f:view>

Takie rozwiązanie ma jednak dość poważną wadę, bowiem wspiera jedynie ziarna zarządzane, których zasięg (scope) dotyczy żądania. Własności ziaren zarządzanych deklarowanych w pliku faces-config.xml muszą mieć czas życia co najmniej taki jak czas życia samego ziarna, toteż #{param}, który żyje tylko wraz z żądaniem ogranicza zasięg całego ziarna. Często jednak zdarza się, że potrzebujemy ziarna żyjącego przez całą sesję, wtedy nie możemy skorzystać z własności deklarowanej w faces-config.xml. Możemy jednak, zamiast oczekiwać wstrzyknięcia wartości naszej własności przez JSF, sami pobrać wartość parametru żądania.

W tym celu usuwamy z pliku faces-config.xml deklarację parametru pozostawiając:

<managed-bean>
    <managed-bean-name>company</managed-bean-name>
    <managed-bean-class>net.schowek.jsf.ui.CompanyController</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
</managed-bean>

Modyfikujemy tez nasze ziarno usuwając własność selectedEmployeeId wraz z getterem i setterem i zmieniając metodę getSelectedEmployee():

public Employee getSelectedEmployee()
{
  Map<String, String> reqParams = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
  String eId = reqParams.get( "e" );
  Long selectedEmployeeId = Long.parseLong( eId );
  if (selectedEmployee == null)
  {
    selectedEmployee = (Employee) companyService.findById( selectedEmployeeId );
  }
  return selectedEmployee;
}

Od tej pory ziarno o czasie życia sesji również potrafi obsługiwać parametry GET, a my możemy dodawać do zakładek odnośniki do kart konkretnych pracowników :)