Pokazywanie postów oznaczonych etykietą JSF. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą JSF. Pokaż wszystkie posty

czwartek, 5 lutego 2009

aplikacja JSF i EJB 3.0 w JBoss Tools

W dzisiejszym odcinku, stworzymy aplikację webową (a jakże) korzystającą z EJB 3.0 i JSF. Ktoś może się zapytać po co? Przecież mamy znakomity szkielet na literę S... Owszem mamy, ale może nie znamy, albo może nie potrzebujemy jego możliwości... Różnie to w życiu bywa. Poza tym modułowa budowa, którą narzuca nam podział aplikacji na webową i biznesową wydaje się całkiem rozsądnym rozwiązaniem.
Zobaczmy zatem, co i jak... Do pracy nad aplikacją użyję środowiska Eclipse 3.3 Europa z zainstalowaną wtyczką JBoss Tools 2.1.2.GA (ostatnia finalna wersja). Jako środowisko uruchomieniowe posłuży mi kontener JBoss AS 4.2.0.GA. Bazą danych będzie MySQL 5.0.
Strukturę aplikacji tworzy archiwum EAR (Enterprise Application Archive) zawierające aplikację EJB (archiwum ejb-jar) oraz aplikację webową (archiwum war).


Ten na pozór skomplikowany podział może okazać się bardzo przydatny w większych, żeby nie powiedzieć korporacyjnych rozwiązaniach. Z warstwy usług zaimplementowanej w aplikacji EJB korzystać może nie tylko nasza aplikacja JSF, ale także inne, niekoniecznie nawet webowe aplikacje.

Stworzona aplikacja, będzie typową aplikacją CRUD, umożliwiającą tworzenie, odczyt, aktualizację i kasowanie pracowników. Potrzebować zatem będziemy 3 projekty. Projekt ejb, jsf oraz zawierający je projekt aplikacji ear.
Zacznijmy od EJB. Klikam File / New / Other...


i z listy wybieram EJB Project.


Podaję nazwę projektu ejbProject i klikam Next > Na następnej zakładce zaznaczam EJB module Java i Java Persistence


klikam 2 razy Next >, odznaczam create orm.xml (będę używał adnotacji) i klikam Finish


Tworzę w projekcie encję Employee

package pl.matt.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;

@NamedQueries({
@NamedQuery(name = "Employee.findAllOrderByName", query = "FROM Employee e ORDER BY e.lastName, e.firstName")
})
@Entity
public class Employee {

private int id;
private String password;
private String username;
private String firstName;
private String lastName;

@Id
@GeneratedValue
public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

@Column(nullable = false)
public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

@Column(nullable = false)
public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

@Column(nullable = false)
public String getFirstName() {
return firstName;
}

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

@Column(nullable = false)
public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

}



warstwę dostępu do danych (DAO):


package pl.matt.dao;

import java.util.List;

import pl.matt.model.Employee;

public interface EmployeeDao {

/**
* @return
*/
public List<Employee> getAllOrderByName();
/**
* @param employee
* @return
*/
public Employee create(Employee employee);

/**
* @param employee
*/
public void update(Employee employee);

/**
* @param employeeId
* @return
*/
public Employee load(int employeeId);

/**
* @param employeeId
* @return
*/
public void delete(Employee employee);

}



package pl.matt.dao.impl;

import java.util.List;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import pl.matt.dao.EmployeeDao;
import pl.matt.model.Employee;

@Stateless
public class JpaEmployeeDao implements EmployeeDao {

@PersistenceContext
private EntityManager entityManager;

@SuppressWarnings("unchecked")
public List<Employee> getAllOrderByName() {
return entityManager.createNamedQuery("Employee.findAllOrderByName").getResultList();
}

public Employee create(Employee employee) {
entityManager.persist(employee);
return employee;
}

public void update(Employee employee) {
entityManager.merge(employee);
}

public Employee load(int employeeId) {
return entityManager.find(Employee.class, employeeId);
}

public void delete(Employee employee) {
entityManager.remove(employee);

}

}


i może trochę na wyrost warstwę usług biznesowych

package pl.matt.service;

import java.util.List;

import pl.matt.model.Employee;

public interface EmployeeService {
/**
* @return
*/
public List<Employee> getAllOrderByName();
/**
* @param employee
* @return
*/
public Employee create(Employee employee);

/**
* @param employee
*/
public void update(Employee employee);

/**
* @param employeeId
* @return
*/
public Employee load(int employeeId);

/**
* @param employeeId
*/
public void delete(int employeeId);
}



package pl.matt.service.impl;

import java.util.List;

import javax.ejb.EJB;
import javax.ejb.Stateless;

import pl.matt.dao.EmployeeDao;
import pl.matt.model.Employee;
import pl.matt.service.EmployeeService;

@Stateless
public class EmployeeServiceImpl implements EmployeeService {

@EJB
private EmployeeDao employeeDao;

public Employee create(Employee employee) {
return employeeDao.create(employee);
}

public List<Employee> getAllOrderByName() {
return employeeDao.getAllOrderByName();
}

public void update(Employee employee) {
employeeDao.update(employee);

}

public Employee load(int employeeId) {
return employeeDao.load(employeeId);
}

public void delete(int employeeId) {
Employee employee = load(employeeId);
if (employee != null) {
employeeDao.delete(employee);
}
}

}


Ponieważ korzystam z bazy danych za pośrednictwem JPA, nie obejdzie się bez pliku META-INF/persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">


<persistence-unit name="seam_war_project" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/empAppDatasource</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show_sql" value="true"/>
</properties>
</persistence-unit>

</persistence>


potrzebuję też źródła danych (Data Source) empAppDatasource. Tworzę ją w pliku empApp-ds.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE datasources
PUBLIC "-//JBoss//DTD JBOSS JCA Config 1.5//EN"
"http://www.jboss.org/j2ee/dtd/jboss-ds_1_5.dtd">

<datasources>

<local-tx-datasource>
<jndi-name>empAppDatasource</jndi-name>
<connection-url>jdbc:mysql://localhost:3306/jpabasics?characterEncoding=latin2</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>root</user-name>
<password>root</password>
</local-tx-datasource>

</datasources>

który umieszczam w katalogu server/default/deploy/ JBossa.

Przejdźmy do aplikacji JSF. Tworzę ją wybierając z menu Eclipse File / New / Other... / JSF Project



Jako środowisko JSF ustawiam JSF 1.2 z Faceletami, podaję nazwę projektu jsfModule i klikam Finish.

W projekcie tworzę prosty szablon stron WebContent/templates/common.xhtml

<!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:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">

<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title><ui:insert name="pageTitle">Page Title</ui:insert></title>
<style type="text/css">
body {
font-family: Verdana, Arial, Helvetica, sans-serif;
font-size: 14px;
}

.header {
font-family: Verdana, Arial, Helvetica, sans-serif;
font-size: 18px;
}

.bottom {
font-family: Verdana, Arial, Helvetica, sans-serif;
font-size: 9px;
text-align: center;
vertical-align: middle;
color: #8E969D;
}

td.column1 {
width: 15%;
}

td.column2 {
width: 25%;
}

td.column3 {
width: 60%;
}
</style>
</head>

<body bgcolor="#ffffff">
<table style="border: 1px solid #CAD6E0" align="center" cellpadding="0"
cellspacing="0" border="0" width="400">
<tbody>

<tr>
<td class="header" height="42" align="center" valign="middle"
width="100%" bgcolor="#E4EBEB"><ui:insert name="pageHeader">Page Header</ui:insert>
</td>
</tr>
<tr>
<td height="1" width="100%" bgcolor="#CAD6E0"></td>
</tr>

<tr>
<td width="100%" colspan="2">
<table width="100%" style="height: 150px" align="left"
cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr>
<td align="left" width="800px" valign="middle"><ui:insert
name="body">Page Body</ui:insert></td>
</tr>
</tbody>
</table>
</td>
</tr>

<tr>
<td colspan="2" valign="bottom" height="1" width="100%"
bgcolor="#CAD6E0"></td>
</tr>
</tbody>
</table>
</body>

</html>


plik wyświetlający listę pracowników WebContent/pages/employeeList.xhtml

<!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:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jstl/core">

<f:loadBundle basename="resources" var="msg" />

<ui:composition template="/templates/common.xhtml">

<ui:define name="pageTitle">Lista pracowników</ui:define>

<ui:define name="pageHeader">Lista pracowników</ui:define>

<ui:define name="body">
<div style="text-align: center"><h:form>
<h:commandLink action="ADD_EMP" value="Dodaj pracownika" />
<br />
<br />
<ui:repeat value="#{employeeBean.employees}" var="emp">
<h:outputLink value="editEmployee.jsf?employeeId=#{emp.id}">
<h:outputText value="#{emp.firstName} #{emp.lastName}" />
</h:outputLink>
<h:outputText value=" " />
<h:commandLink action="#{employeeBean.delete}" value="[usuń]">
<f:param name="toDelete" value="#{emp.id}" />
</h:commandLink>
<br />
</ui:repeat>

</h:form></div>

</ui:define>
</ui:composition>
</html>


oraz plik umożliwiający edycję pojedynczego pracownika WebContent/pages/editEmployee.xhtml

<!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:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jstl/core">

<f:loadBundle basename="resources" var="msg" />

<ui:composition template="/templates/common.xhtml">

<ui:define name="pageTitle">Edycja</ui:define>

<ui:define name="pageHeader">Edycja pracownika</ui:define>

<ui:define name="body">
<h:outputLink value="employeeList.jsf">Powrót</h:outputLink>
<h:form id="employeeForm">
<h:inputHidden id="employeeId" value="#{employeeBean.employee.id}" />
<h:panelGrid columns="3" width="800"
columnClasses="column1,column2,column3">
<h:outputText value="Imię" />
<h:inputText value="#{employeeBean.employee.firstName}"
id="firstName" required="true" />
<h:message for="firstName" style="color:red;" />
<h:outputText value="Nazwisko" />
<h:inputText value="#{employeeBean.employee.lastName}" id="lastName"
required="true" />
<h:message for="lastName" style="color:red;" />
<h:outputText value="Login" />
<h:inputText value="#{employeeBean.employee.username}" id="username"
required="true" />
<h:message for="username" style="color:red;" />
<h:outputText value="haslo" />
<h:inputSecret id="password"
value="#{employeeBean.employee.password}" required="true" redisplay="true" />
<h:message for="password" style="color:red;" />
<h:commandButton action="#{employeeBean.save}" value="zapisz" />
</h:panelGrid>
<h:outputLink value="employeeList.jsf">Powrót</h:outputLink>
</h:form>
</ui:define>
</ui:composition>
</html>


Potrzebne będzie też ziarno zarządzane JSF

package pl.matt.view;

import java.util.List;

import javax.ejb.EJB;
import javax.faces.context.FacesContext;

import pl.matt.model.Employee;
import pl.matt.service.EmployeeService;

public class EmployeeBean {

private List<Employee> employees;
private Employee employee;
private Integer employeeId;

@EJB(name="eeProject/EmployeeServiceImpl/local")
private EmployeeService employeeService;

public List<Employee> getEmployees() {
if (employees == null) {
employees = employeeService.getAllOrderByName();
}
return employees;
}

public Employee getEmployee() {
if (employee == null) {
if (employeeId != null) {
employee = employeeService.load(employeeId);
} else {
employee = new Employee();
}
}
return employee;
}

public void setEmployee(Employee employee) {
this.employee = employee;
}

public String save() {
if (employee.getId() > 0) {
employeeService.update(employee);
} else {
employeeService.create(employee);
}
employees = null;
return "LIST_EMP";
}

public Integer getEmployeeId() {
return employeeId;
}

public void setEmployeeId(Integer employeeId) {
this.employeeId = employeeId;
}

public String delete() {
System.out.println("EmployeeBean.delete()");
Integer id = Integer.valueOf(
FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("toDelete"));
if (id != null) {
employeeService.delete(id);
}
employees = null;
return "LIST_EMP";
}

}



JBoss umieszcza w interfejsie JNDI ziarna EJB korzystając z trochę dziwnej konwencji:
nazwaModułu/klasaZiarna/local|remote stąd konieczność podania parametru name
@EJB(name="eeProject/EmployeeServiceImpl/local")

przy adnotacji @EJB.
Jak będzie wyglądała sytuacja w momencie, kiedy ziarno EJB będzie implementowało kilka interfejsów zdalnych lub lokalnych? Niestety nie mam pojęca.

Do kompletu brakuje jeszcze pliku WebContent/WEB-INF/faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">
<managed-bean>
<managed-bean-name>employeeBean</managed-bean-name>
<managed-bean-class>pl.matt.view.EmployeeBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>employeeId</property-name>
<value>#{param.employeeId}</value>
</managed-property>
</managed-bean>
<navigation-rule>
<navigation-case>
<from-outcome>ADD_EMP</from-outcome>
<to-view-id>/pages/editEmployee.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>LIST_EMP</from-outcome>
<to-view-id>/pages/employeeList.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<application>
<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
</application>
</faces-config>


oraz WebContent/WEB-INF/web.xml

<?xml version="1.0"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<description>Facelets StarterKit</description>
<display-name>jsfModule</display-name>
<context-param>
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
<param-value>.xhtml</param-value>
</context-param>
<context-param>
<param-name>facelets.REFRESH_PERIOD</param-name>
<param-value>2</param-value>
</context-param>
<context-param>
<param-name>facelets.DEVELOPMENT</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<context-param>
<param-name>com.sun.faces.validateXml</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>com.sun.faces.verifyObjects</param-name>
<param-value>true</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
</web-app>


Aplikacja JSF gotowa. Aplikacja EJB gotowa. Czas je wyswatać i pożenić. Pomoże nam w tym projekt EAR. Z menu kontekstowego Eclipse wybieram zatem File / New / Other... / Enterprise Application Project.


Podaję nazwę projektu eeProject klikam 2 razy Next > i wybieram oba stworzone uprzednio moduły. Będą to składowe naszej aplikacji. Dodatkowo zaznaczam opcję Generate Deployment Descriptor i klikam Finish.



W tym projekcie znajduje się tylko plik EarContent/META-INF/application.xml.


<?xml version="1.0" encoding="UTF-8"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:application="http://java.sun.com/xml/ns/javaee/application_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd" id="Application_ID" version="5">
<module>
<ejb>ejbProject.jar</ejb>
</module>
<module>
<web>
<web-uri>jsfModule.war</web-uri>
<context-root>jsfModule</context-root>
</web>
</module>
</application>


Plik ten opisuje moduły naszej aplikacji. Moduł webowy jsfModule i moduł EJB ejbProject.jar. Wdrożenie aplikacji pozostawiam wtyczce JBoss Tools. Startuję więc serwer i... aplikacja działa.



Kod źródłowy powyższej aplikacji dostępny jest tutaj.

Cóż, pora jechać na narty...

czwartek, 4 września 2008

JBoss Tools - nowa jakość w tworzeniu aplikacji opartych o JSF

Całkiem niedawno, Piotrek Buczek, jeden z moich zafascynowanych Seamem kolegów pokazał mi kilka sztuczek związanych z tą technologią. Oprócz sztuczek, bardzo zresztą widowiskowych uwagę moją przykuło środowisko na jakim pracował. Był to Eclipse 3.3 z wtyczką JBoss Tools. Wsparcie dla Seama - bardzo pierwsza liga, jak to mawia pan Kazimierz. Jak się ma wsparcie dla samych JSFów, zaraz zobaczymy. Zdradzę tylko, że wcale nie gorzej...

Odkąd dostałem olbrzymią emkę za udział w konkursie Ganymede Around the World Contest nie przystoi mi używać innej wersji Eclipse. Na nim zatem zainstaluję wtyczkę JBoss Tools. Z Ganymede działa tylko rozwojowa wersja wtyczki (JBossTools 3.0.0.Alpha1) więc tą właśnie zainstaluję, ale po kolei...

Wtyczkę można pobrać ze strony projektu JBoss Tools lub zainstalować wykorzystując Eclipsowego zarządcę wtyczek. Wybieram to drugie rozwiązanie. Otwieram menu
Help / Software Updates...
i wybieram zakładkę
Avaliable Software.
Dodaję nową stronę o adresie:
http://download.jboss.org/jbosstools/updates/development



Rozwijam nowo utworzony węzeł drzewka, zaznaczam
JBossTools Development Release 3.0.0.Alpha1
i klikam magiczny przycisk
Install



Eclipse warczy, ściąga, restartuje się... i gotowe. Wypada jeszcze tylko skonfigurować w Eclipse JBossa. Odpalam zatem menu
Window / Preferencess. i tam wybieram pozycję
Server / Runtime Environments następnie klikam
Add... i rozwijam gałąź JBoss, a division of Red Hat.


Kilka razy klikam Next wskazuję położenie na dysku JBossa i gotowe.

Czas przyjrzeć się lepiej możliwościom wtyczki.Tworzę nowy projekt JSF (File / New... / Project... / JSF Project).


Na następnej zakładce wybieram wersję JSF 1.2 z Faceletami oraz szablon FaceletsKickStartWithoutLibs, jako, że biblioteki mam w JBossie.




Zobaczmy co oferuje wtyczka. Otwieram plik WebContent/pages/greeting.xhtml. Pierwsze zaskoczenie - oprócz kodu pliku xhtml, mam zgrabny podgląd.



Klikam dowolne miejsce na podglądzie, podświetla mi się odpowiednie miejsce w kodzie.



Ładnie...
Dodaję nowy tag, zmiany na bieżąco widzę w oknie podglądu.



Jak dla mnie, rewelacja...
Czas na zabawy z klawiszem CTRL. Przyciskam go, najeżdżam myszą na pole name wyrażenia #{person.name}. Klikam..,. i już widzę metodę getName() klasy Person, która w pliku faces-config.xml skonfigurowana jest jako ziarno zarządzane JSF.



Klikam w słówko greeting wyrażenia #{msg.greeting}. No i bomba, jestem już przy edycji pliku messages.properties i jego właściwości greeting.



Plik oczywiście jest skonfigurowany odpowiednio w faces-config.xml.
Przejdźmy zatem do tego pliku. Już pierwszy rzut oka, pozwala nam się zorientować, że mamy możliwość graficznej edycji nawigacji w aplikacji JSF.



Sympatycznie. Tu niestety mały minus... Wtyczka automatycznie formatuje zawartość xmlową pliku faces-config.xml, więc nie bardzo można mieć kontrolę nad wcięciami i kolejnością tagów w pliku. Przynajmniej ja nie potrafię tego osiągnąć.

Wrócę jeszcze na moment do pliku WebContent/pages/greeting.xhtml. Oprócz nawigacji z klawiszem CTRL. Wtyczka JBoss Tools oferuje całkiem niezłe podpowiadanie. Zarówno tagów (wystarczy wpisać <f: i nacisnąć CTRL+spację)



jak i właściwości ziaren zarządzanych



oraz wpisów w pliku messages.properties.



Mało? Zobaczmy, co jeszcze oferuje JBoss Tools. Odpalam projekt (na zakładce JBoss Server View klikam Start the server.



Serwer startuje...
Wchodzę na stronę
http://localhost:8080/jsfProject/

otwieram w Eclipsie plik
WebContent/pages/inputname.xhtml. Zauważyliście, że wykorzystywana jest tu, trochę zapomniana funkcja Faceletów, która umożliwia takie konstruowanie stron JSF, aby były one poprawnie wyświetlane w przeglądarce WWW bez uruchamiania ich na serwerze aplikacji?
Zmieniam tytuł Facelets Hello Application na Witaj!. Zapisuję stronę. Przeładowuję ją w przeglądarce... i gotowe, zmiany od razu są widoczne.



Niestety zmiany w kodzie źródłowym, w plikach messages*.properties lub konfiguracja w faces-config.xml wymagają już restartu serwera.



To zapewne tylko niektóre możliwości wtyczki. Jak dla mnie, są one rewelacyjne. Coś czuję, że JBoss Tools dołączy do mojego zestawu ulubionych wtyczek środowiska Eclipse.