Nous allons voir dans ce billet comment réaliser simplement une application JSF en utilisant Generic System dans un environnement JEE avec Wildfly.
Cette application est récupérable depuis le repository GIT (ici).
Configuration Environnement
Maven
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.genericsystem</groupId> <artifactId>gs-example-jsf</artifactId> <version>3.0-SNAPSHOT</version> <packaging>war</packaging> <name>gs-example-jsf</name> <properties> <maven-compiler-plugin.version>3.2</maven-compiler-plugin.version> <java.version>1.8</java.version> <jsf.version>2.1.19</jsf.version> <jstl.version>1.2</jstl.version> <weld.core.version>2.2.6.Final</weld.core.version> </properties> <repositories> <repository> <id>middlewarefactory</id> <url>http://genericsystem.org/repository</url> <releases> <enabled>true</enabled> <updatePolicy>daily</updatePolicy> </releases> <snapshots> <enabled>true</enabled> <updatePolicy>daily</updatePolicy> </snapshots> </repository> <repository> <id>jboss-nexus</id> <name>JBoss (Nexus) Stable Repository</name> <url>https://repository.jboss.org/nexus/content/groups/public-jboss</url> <releases> <enabled>true</enabled> <updatePolicy>daily</updatePolicy> </releases> <snapshots> <enabled>true</enabled> <updatePolicy>daily</updatePolicy> </snapshots> </repository> </repositories> <dependencies> <dependency> <groupId>org.genericsystem</groupId> <artifactId>gs-cdi</artifactId> <version>3.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.jboss.weld.servlet</groupId> <artifactId>weld-servlet</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.jboss.spec.javax.faces</groupId> <artifactId>jboss-jsf-api_2.2_spec</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>${jstl.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.el</groupId> <artifactId>el-api</artifactId> <version>2.2</version> <scope>provided</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.jboss.weld</groupId> <artifactId>weld-core-bom</artifactId> <version>${weld.core.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <finalName>${project.artifactId}</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>${maven-compiler-plugin.version}</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin> </plugins> </build> </project> |
Web.xml
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 |
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <context-param> <param-name>javax.faces.DEFAULT_SUFFIX</param-name> <param-value>.xhtml</param-value> </context-param> <session-config> <session-timeout>30</session-timeout> </session-config> <welcome-file-list> <welcome-file>pages/index.xhtml</welcome-file> </welcome-file-list> <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>*.xhtml</url-pattern> </servlet-mapping> </web-app> |
beans.xml
Fichier nécessaire pour autoriser CDI à scanner les classes de l’archive.
1 2 3 4 |
<?xml version="1.0" encoding="UTF-8"?> <beans 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/beans_1_0.xsd"> </beans> |
Configuration Generic System
Par défaut, Generic System n’est pas persistant. Pour activer cette fonctionnalité nous devons spécialiser le bean PersistentDirectoryProvider pour que la méthode getDirectoryPath() retourne le répertoire où seront enregistrées les sauvegardes. Nous créons donc une classe java MyPersistentDirectoryProvider :
L’annotation Specializes est une annotation CDI (voir la documentation ici)
1 2 3 4 5 6 7 8 9 10 11 12 |
@Specializes public class MyPersistentDirectoryProvider extends PersistentDirectoryProvider { @Override public String getEngineValue() { return Statics.ENGINE_VALUE;//Engine name } @Override public String getDirectoryPath() { return DIRECTORY_PATH;//Your directory path } } |
Modèle de données
Nous utilisons la configuration statique de Generic System pour mettre en place notre modèle de données. Pour cela, nous créons deux classes : Car et Power.
Car.java
Ici Car est un type, afin de le spécifier à GS il nous suffit d’ajouter l’annotation @SystemGeneric.
1 2 |
@SystemGeneric public class Car { } |
Power.java
Ici Power est une propriété de Car qui prend comme valeur des entiers. Pour le spécifier à GS nous utiliserons :
L’annotation @Components qui permet de définir notre composant Car,
L’annotation @PropertyConstraint qui permet de spécifier que c’est une propriété,
Et enfin l’annotation @InstanceValueClassConstraint qui permet de restreindre la classe des valeurs (ici des entiers).
1 2 3 4 5 |
@SystemGeneric @Components(Car.class) @PropertyConstraint @InstanceValueClassConstraint(Integer.class) public class Power { } |
Afficher les voitures
Pour afficher les voitures, nous avons créé un bean CarBean qui possède une méthode getCars et une référence à l’Engine.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
@Named @RequestScoped public class CarBean { @Inject private Engine engine; private Generic car; @PostConstruct public void init() { car = engine.find(Car.class); } public List<Generic> getCars() { return car.getAllInstances().stream().collect(Collectors.toList()); } } |
Voyons maintenant notre page xhtml (index.xhtml) qui permet d’afficher ces voitures :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <h:head /> <h:body> <h:messages id="message" /> <h:form> <h:dataTable value="#{carBean.cars}" var="car"> <h:column> <f:facet name="header">Car</f:facet> <h:outputText value="#{car.getValue()}" /> </h:column> </h:dataTable> </h:form> </h:body> </html> |
N’ayant aucune voiture pour le moment, notre page est vide. Voyons maintenant comment la remplir en ajoutant des voitures.
Ajout de voitures
Ajoutons a notre bean CarBean la fonctionnalité d’ajout de voiture.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
[...] private Generic power; private String newCarName; // + getter and setter private Integer newCarPower; // + getter and setter @PostConstruct public void init() { car = engine.find(Car.class); power = engine.find(Power.class); } public String addCar() { car.setInstance(newCarName).setHolder(power, newCarPower); return "#"; } [...] |
Et modifions notre page xhtml :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[...] <h:form> <h:dataTable value="#{carBean.cars}" var="car"> <h:column> <f:facet name="header">Car</f:facet> <h:outputText value="#{car.getValue()}" /> </h:column> <f:facet name="footer"> <h:panelGroup id="addCar" layout="block"> <h:inputText size="10" value="#{carBean.newCarName}" /> <h:inputText size="10" value="#{carBean.newCarPower}"> <f:convertNumber integerOnly="true" /> </h:inputText> <h:commandButton value="Add" action="#{carBean.addCar}"> <f:ajax execute="addCar" render="@form" /> </h:commandButton> </h:panelGroup> </f:facet> </h:dataTable> </h:form> [...] |
Voyons le résultat en images :
Affichage et modification de la puissance
Pour afficher et modifier la puissance nous ajoutons une méthode au bean CarBean permettant de récupérer (getValue) et assigner (setValue) la valeur de l’attribut power pour l’instance voiture.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public static interface ValueExpressionWrapper { public String getValue(); public void setValue(String value); } public ValueExpressionWrapper getPower(Generic instance) { return new ValueExpressionWrapper() { @Override public String getValue() { // Power is a property constraint return Objects.toString(instance.getValues(power).first()); } @Override public void setValue(String value) { // The value power must be an integer due the InstanceValueClassConstraint instance.setHolder(power, Integer.parseInt(value)); } }; } |
Nous modifions la puissance de la voiture au moment de la sortie de l’input text par un appel AJAX, voyons la page xhtml :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[...] <h:column> <f:facet name="header">Car</f:facet> <h:outputText value="#{car.getValue()}" /> </h:column> <h:column> <f:facet name="header">Power</f:facet> <h:inputText size="10" value="#{carBean.getPower(car).value}"> <f:convertNumber integerOnly="true" /> <f:ajax event="blur" execute="@this" render="@this" /> </h:inputText> </h:column> [...] |
Suppression d’une voiture
Mettons maintenant en place la fonctionnalité de suppression.
1 2 3 4 |
public String deleteCar(Generic car) { car.remove(); return "#"; } |
1 2 3 4 5 6 7 |
<h:dataTable value="#{carBean.cars}" var="car"> <h:column> <h:commandButton value="x" action="#{carBean.deleteCar(car)}"> <f:ajax execute="@this" render="@form" /> </h:commandButton> </h:column> [...] |
Enregistrement et annulation
L’enregistrement ne se fait actuellement que sur le cache, il faut donc ajouter un bouton pour flusher les modifications et un autre pour les annuler.
1 2 3 4 5 6 7 8 9 |
public String flush() { engine.getCurrentCache().flush(); return "#"; } public String clear() { engine.getCurrentCache().clear(); return "#"; } |
1 2 3 4 5 6 7 8 9 |
[...] <h:commandButton value="Save" action="#{carBean.flush}"> <f:ajax execute="@this" render="@form" /> </h:commandButton> <h:commandButton value="Cancel" action="#{carBean.clear}"> <f:ajax execute="@this" render="@form" /> </h:commandButton> </h:form> [...] |
Dans le prochain billet nous verrons comment ajouter une valeur par défaut à une relation.