Facelets case study: Ricettario
Docente: Gabriele Lombardi
[email protected]
Sommario
SLIDE
CONTENUTO
Scopo
Descrizione dello scopo del mini-progetto.
Architettura
Come utilizzare JEE per questo progetto.
Modello dei dati
Un domain-model mappato con JPA.
Business layer
Funzionalità ed esposizione con WS.
Interfaccia web
Componenti facelet e utilizzo di primefaces.
Scopo

Realizzare una piccola applicazione web che:
– permetta di creare/editare/eliminare/cercare delle
ricette di cucina memorizzate in un database;
– sia realizzata secondo il modello three-tier;
– abbia un’interfaccia web MVC modulare;
– offra le proprie funzionalità anche via web-service.

Strumenti utilizzati (in GlassFish):
–
–
–
–
JPA per la persistenza;
EJB per il business-tier;
Facelet con componenti custom per l’app web;
JAX-WS per i web-services;
Architettura
Business tier
JavaDB
Endpoint
JAX-WS
EJB
EJBcon
con
logica
logicadidi
business
business
Persistence
provider
Data tier
Modello dei dati come JavaBeans
TipoPiatto
Ricetta
Injection
Injection
ORM con JPA
Controller
Controllerdidi
pagina
paginaJSF
JSF
Espressioni EL
Pagina
PaginaJSF
JSF
con
consintassi
sintassi
Facelet
Facelet
Ingrediente
Controller
Controllerdidi
componente
componente
Componente
Componente Espressioni EL
composita
composita
Facelet
Facelet
Presentation tier
Modello dei dati



Utilizziamo dei «normali» JavaBeans;
mappiamo la struttura dati sul DB con JPA;
mappiamo la struttura dati sull’XML con JAX-B.
@MappedSuperclass
public class DomainObject implements Serializable {
@Id @GeneratedValue
private Long id;
…
}
@Entity @XmlAccessorType(XmlAccessType.FIELD)
public class Recipe extends DomainObject {
@Column(nullable = false, length = 64)
@XmlAttribute(required = true)
private String name;
@Lob @Column(nullable = false)
private String description;
@Entity
public class Ingredient extends DomainObject{
@Column(nullable = false, length = 64)
private String name;
@Enumerated
private PlateType plateType;
@OneToMany(cascade = CascadeType.ALL)
@XmlElementWrapper(name = "ingredients")
@XmlElement(name = "ingredient")
private List<Ingredient> ingredients;
@Lob
private String comment;
@Column(nullable = false, length = 16)
private String quantity;
…
}
…
}
Modello dei dati


Configurazione di JPA  in persistence.xml
Configurazione del DataSource JNDI:
– in GlassFish (o con file apposito di configurazione).
<persistence version="2.0" xmlns=“…" xmlns:xsi=“…" xsi:schemaLocation=“... …">
<persistence-unit name="RicettarioCorePU" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/Ricettario</jta-data-source>
<mapping-file>com/gab/tests/j2ee/ricettario/core/entities/queries.xml</mapping-file>
<properties>
<property name="eclipselink.ddl-generation" value="create-tables"/>
</properties>
</persistence-unit>
</persistence>
Connection pool
Risorsa JNDI

Business layer
Definire le query in un file XML:
– in persistence.xml abbiamo detto:
– <mapping-file>…/queries.xml</mapping-file>
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns=“…“ xmlns:xsi=“…“ xsi:schemaLocation=“… …" version="1.0">
<package>com.gab.tests.j2ee.ricettario.core.entities</package>
<named-query name="Recipe.getAll">
<query>SELECT r FROM Recipe r</query>
</named-query>
<named-query name="Recipe.getNameLike">
<query>
SELECT r FROM Recipe r
WHERE lower(r.name) LIKE lower(:namePart)
</query>
</named-query>
Tutte le ricette
Ricette con like sul
nome (parametro)
<named-query name="Recipe.getIngredientLike">
<query>
SELECT r FROM Recipe r, IN(r.ingredients) i
WHERE lower(i.name) LIKE lower(:namePart)
</query>
</named-query>
</entity-mappings>
Ricette con like sul
nome dell’ingrediente
Business layer

Implementazione di un EJB:
– Stateless  ogni richiesta è indipendente dalle altre:
– è il tipo di EJB di sessione (con risultato) più efficiente, uno può
essere condiviso tra svariati client in thread concorrenti.
– annotato con JAX-WS per offrirsi come WebService;
– con iniezione per l’accesso alla persistenza;
– i metodi sono automaticamente transazionali (JTA).
@Local public interface RecipesBean {
public Recipe save(Recipe recipe);
public List<Recipe> getAll();
public List<Recipe> getNameLike(String namePart);
public List<Recipe> getIngredientNameLike(String namePart);
public void delete(Recipe recipe);
}
@Stateless @WebService public class RecipesBeanImpl implements RecipesBean {
@PersistenceContext(unitName = "RicettarioCorePU")
private EntityManager em;
@Override public Recipe save(Recipe recipe) {…}
@Override @WebResult(name = "recipe") public List<Recipe> getAll() {…}
@Override public void delete(@WebParam(name = "recipe") Recipe recipe) {…}
@Override public List<Recipe> getNameLike(String namePart) {…}
@Override public List<Recipe> getIngredientNameLike(String namePart) {
return em
.createNamedQuery("Recipe.getIngredientLike", Recipe.class)
.setParameter("namePart", "%" + namePart.trim() + "%").getResultList();
}
}
Interfaccia Web: la componente

Ha un controller proprio:
– ne implementa le operazioni e ne mantiene lo stato.

Ha una view propria:
– definita con sintassi Facelet usando il namespace cc.
@FacesComponent("recipeEditor")
public class RecipeEditor
extends UIPanel
implements NamingContainer {
private Recipe recipe;
public Recipe getRecipe() {
if (recipe == null) {
recipe = (Recipe)getValueExpression("value")
.getValue(FacesContext.getCurrentInstance()
.getELContext());
}
return recipe;
}
@Override public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
…
// Operazioni implementate qui
}
<cc:interface componentType="recipeEditor">
<cc:attribute name="value" required="true"
type="com.gab.tests.j2ee.ricettario.core.entities.Recipe"/>
</cc:interface>
<!-- IMPLEMENTATION -->
<cc:implementation>
…
<p:commandButton icon="ui-icon-closethick"
title="Elimina“ immediate="true"
process="@this" update="@form"
onclick="if (!confirm('Sicuro?')) { return false; }"
action="#{cc.removeIngredient(ingredient)}"/>
</p:column>
</p:dataTable>
<h:outputText value="Descrizione:"/>
<p:inputTextarea value="#{cc.recipe.description}"/>
</h:panelGrid>
</cc:implementation>
Interfaccia Web: Il controller di pagina

La pagina:
– è una facelet «normale» che utilizza la componente;
– il controller da accesso allo strato di business.
@ManagedBean(name = "recipes") @ViewScoped
public class RecipesController implements Serializable {
// Iniezione dell’EJB con lo strato di business.
@EJB private RecipesBean rb;
// Attributi ed operazioni qui che usano l’EJB.
}
© 2013 - ECOLE
Interfaccia Web
Scarica

xx_Ricettario_CaseSt..