Nous abordons maintenant l'écriture du client web mobile.
Le client web mobile permet de gérer les Arduinos à distance avec le navigateur d'un smartphone ou d'une tablette. Il présente à l'utilisateur les écrans suivants :
La vue d'accueil [1] propose les opérations possibles. La vue [pinWrite] [2] permet d'écrire une valeur sur une pin d'un Arduino.
La vue [pinRead] [1] permet de lire la valeur d'une pin d'un Arduino. La vue [blink] [2] permet de faire clignoter une led d'un Arduino :
La vue [commands] [1] permet d'envoyer une commande JSON à un Arduino. De chaque vue, on peut revenir à la vue d'accueil avec l'icône [2].
L'architecture du client web mobile est la suivante :
- la couche [DAO] communique avec le serveur REST. C'est un client REST implémenté par Spring-Android ;
- la couche [web] est une couche JSF2 (Java Server Faces) avec la bibliothèque Ajax PFM (Primefaces Mobile).
La couche [DAO] dialogue avec le serveur REST des Arduinos :
Le serveur REST des Arduinos est exécuté sur un serveur Tomcat. Pour l'application web mobile, nous utiliserons le serveur Glassfish. En mode développement on est amené à redéployer souvent les applications sur les serveurs. Or redéployer une application sur le serveur Tomcat relance Tomcat lui-même donc l'application REST des Arduinos. En mettant le client web mobile sur Glassfish on évite de relancer le serveur REST.
Pour que les serveurs Glassfish et Tomcat puissent fonctionner ensemble, il faut qu'ils travaillent sur des ports différents. Or par défaut, ils travaillent tous les deux sur le port 8080. On pourra changer le port du serveur Tomcat de la façon suivante :
- en [1], dans la fenêtre Servers double-cliquer sur le serveur Tomcat ;
- en [2], modifier son port HTTP.
Les projets Eclipse suivent l'architecture ci-dessus :
En [1] les deux projets Eclipse du client web mobile :
- [webmobile-restClient] : projet de la couche [DAO] ;
- [webmobile-pfm] : le projet web Primefaces Mobile
Dans l'architecture du client web mobile
La couche [DAO] dialogue avec le serveur REST des Arduinos :
La couche [DAO] est un client REST comme l'étaient les couches [métier] et [DAO] du projet Android (paragraphe , page et paragraphe , page ). On y trouve du code analogue.
XII-D-1. Le projet Eclipse▲
Le projet Eclipse de la couche [DAO] est le suivant :
- en [1], le projet [webmobile-restClient] est un projet Maven ;
- en [2], les dépendances Maven ;
- en [3], l'implémentation du client REST ;
XII-D-2. Les dépendances Maven▲
Le fichier [pom.xml] du projet est le suivant :
Sélectionnez 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.
<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>istia.st.domotique.webmobile</groupId>
<artifactId>webmobile-restClient</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>webmobile-restClient</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>3.1.1.RELEASE</spring.version>
<jackson.mapper.version>1.5.6</jackson.mapper.version>
</properties>
<repositories>
<repository>
<id>com.springsource.repository.bundles.release</id>
<name>SpringSource Enterprise Bundle Repository - Release</name>
<url>http://repository.springsource.com/maven/bundles/release</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>${jackson.mapper.version}</version>
</dependency>
<dependency>
<groupId>istia.st.domotique.common</groupId>
<artifactId>domotique-entities</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- lignes 5-7 : l'identité Maven du projet ;
- lignes 29-33 : Pour le client REST, on a besoin du framework SpringMVC ;
- lignes 34-38 : la dépendance sur la bibliothèque JSON Jackson ;
- lignes 39-43 : le client et le serveur REST échangent des entités définies dans le projet Maven [domotique-entities].
XII-D-3. L'implémentation du client REST▲
Le client REST offre à la couche [web], l'interface [IMetier] suivante :
Sélectionnez 1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
package client.rest.metier;
...
public interface IMetier {
public Collection<Arduino> getArduinos();
public Reponse pinRead(String idCommande, String idArduino, int pin, String mode);
public Reponse pinWrite(String idCommande, String idArduino, int pin, String mode,int val);
public Reponse faireClignoterLed(String idCommande, String idArduino, int pin, int millis, int nbIter);
public List<String> sendCommandesJson(String idArduino, List<String> commandes);
public List<Reponse> sendCommandes(String idArduino, List<Commande> commandes);
}
C'est l'interface de la couche [métier] du serveur. C'est normal. Lorsqu'on met bout à bout le client et le serveur, on voit que la couche [web] du client dialogue avec la couche [métier] du serveur.
L'implémentation [Metier] de cette interface pourrait être la suivante :
Sélectionnez 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.
package client.rest.metier;
...
@Service
public class Metier implements IMetier {
@Autowired
private RestTemplate restTemplate;
private Gson gson = new Gson();
private String urlServiceRest;
public Metier() {
}
public Collection<Arduino> getArduinos() {
...
}
public Reponse pinRead(String idCommande, String idArduino, int pin, String mode) {
...
}
public Reponse pinWrite(String idCommande, String idArduino, int pin, String mode, int val) {
...
}
public Reponse faireClignoterLed(String idCommande, String idArduino, int pin, int millis, int nbIter) {
...
}
@SuppressWarnings("unchecked")
public List<String> sendCommandesJson(String idArduino, List<String> commandes) {
...
}
public List<Reponse> sendCommandes(String idArduino, List<Commande> commandes) {
...
}
public String executeRestService(String method, String urlService, Object request, Map<String, String> paramètres) {
....
}
...
}
Vous connaissez cette implémentation. C'est celle que vous avez écrite pour le client Android. Revoyez les couches [métier] au paragraphe , page et [DAO] au paragraphe , page du projet Android. Il y a une différence : la bibliothèque JSON utilisée est Jackson et non Gson.
: écrire la classe [RestMetier].
XII-D-4. Les tests du client REST▲
- dans le dossier [support] du TP, vous trouverez un projet Eclipse [webmobile-console] [1] ;
- importez-le dans Eclipse. C'est un projet Maven [2] ;
- en [3], les dépendances Maven du projet. Celui-ci dépend du client REST [webmobile-restClient]. Les autres dépendances en découlent ;

- en [4], vous trouverez quatre applications Java testant certaines fonctionnalités du client REST ;
- les quatre applications utilisent Spring pour instancier le client REST [5].
Examinons le code de la classe [ListArduinos] qui affiche la liste des Arduinos connectés :
Sélectionnez 1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
package arduino.restClient.console;
...
public class ListArduinos {
public static void main(String[] args) {
IMetier metier = (IMetier) new ClassPathXmlApplicationContext("spring-restClient.xml").getBean("metier");
System.out.println("Liste des Arduinos connectés");
Collection<Arduino> arduinos = metier.getArduinos();
if (arduinos.size() == 0) {
System.out.println("Aucun Arduino n'est connecté actuellement...");
} else {
for (Arduino arduino : arduinos) {
System.out.println(arduino);
}
}
}
}
- ligne 7 : le client REST est instancié ;
- ligne 9 : on lui demande la liste des Arduinos connectés ;
Le fichier de configuration de Spring [] est le suivant :
Sélectionnez 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"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:oxm="http://www.springframework.org/schema/oxm"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd" xmlns:util="http://www.springframework.org/schema/util">
<bean id="metier" class="client.rest.metier.Metier">
<property name="urlServiceRest" value="localhost:8081/server-rest"/>
<property name="restTemplate" ref="restTemplate"/>
</bean>
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<ref bean="jacksonConverter"/>
<ref bean="stringConverter"/>
</list>
</property>
</bean>
<bean id="jacksonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
<bean id="stringConverter" class="org.springframework.http.converter.StringHttpMessageConverter"/>
</beans>
- ligne 12 : définition du client REST ;
- ligne 13 : injection de l'URL du service REST distant. Vous serez peut-être amenés à modifier cette valeur ;
- ligne 14 : injection du client REST de Spring ;
- ligne 17 : le client REST de Spring. La classe appartient au paquetage [org.springframework] ;
- ligne 18 : on injecte dans cette classe une liste de convertisseurs de messages. Cette notion a été expliquée au paragraphe , page .
- ligne 25 : le convertisseur des messages JSON. On utilise ici la bibliothèque Jackson ;
Travail à faire : Passer les quatre tests du client REST.
A lire :
- le cours JSF (Java Server Faces) de [ref1] ;
XII-E-1. Le projet Eclipse▲
Le projet Eclipse de la couche [web] est le suivant :
- en [1], le projet est un projet Maven ;
- en [2], les dépendances Maven du projet. Celui-ci dépend uniquement du client REST [webmobile-restClient]. Les autres dépendances en découlent ;
- en [3], les classes du projet :
- le paquetage [arduino.jsf.beans] contient les trois beans JSF de l'application de portées respectives Application, Session et Request,
- le paquetage [com.corejsf.util] est un paquetage utilitaire pour gérer l'internationalisation des messages. Nous ne l'expliciterons pas ici. C'est fait dans le cours JSF de [ref1] ;
XII-E-2. Le projet Maven▲
Le fichier [pom.xml] est le suivant :
Sélectionnez 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.
<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>istia.st.domotique.webmobile</groupId>
<artifactId>webmobile-pfm</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>server-pfm</name>
<properties>
<endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1.1-b04</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.1.1-b04</version>
</dependency>
<dependency>
<groupId>org.primefaces</groupId>
<artifactId>mobile</artifactId>
<version>0.9.3</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>istia.st.domotique.webmobile</groupId>
<artifactId>webmobile-restClient</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
...
</build>
<repositories>
<repository>
<id>prime-repo</id>
<name>PrimeFaces Maven Repository</name>
<url>http://repository.primefaces.org</url>
<layout>default</layout>
</repository>
</repositories>
</project>
- lignes 5-7 : l'identité Maven du projet ;
- lignes 18-27 : les dépendances vis à vis des bibliothèques JSF ;
- lignes 28-33 : les dépendances vis à vis de la bibliothèque Primefaces Mobile ;
- lignes 34-38 : la dépendance sur le client REST ;
- lignes 46-51 : le dépôt MAven où peut être trouvée la bibliothèque Primefaces Mobile.
XII-E-3. La configuration de la couche [web]▲
- la configuration et les pages XHTML de la couche [web] sont dans le dossier [webapp] [1] ;
- le projet est configuré par les fichiers [web.xml] et [faces-config.xml] [2].
XII-E-4. Le fichier [web.xml]▲
Le fichier [web.xml] est le suivant :
Sélectionnez 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.
<?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" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<context-param>
<param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</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>/faces/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file>
</welcome-file-list>
</web-app>
C'est le fichier standard de configuration d'une application web JSF. Il n'y a rien ici de spécifique à Primefaces Mobile.
XII-E-5. Le fichier de configuration [faces-config.xml]▲
Le fichier [faces-config.xml] configure les différents éléments d'une application JSF. Il est ici le suivant :
Sélectionnez 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.
<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.0"
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-facesconfig_2_0.xsd">
<application>
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
<message-bundle>messages</message-bundle>
<default-render-kit-id>PRIMEFACES_MOBILE</default-render-kit-id>
</application>
<managed-bean>
<managed-bean-name>applicationData</managed-bean-name>
<managed-bean-class>arduino.jsf.beans.ApplicationData</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>sessionData</managed-bean-name>
<managed-bean-class>arduino.jsf.beans.SessionData</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>requestData</managed-bean-name>
<managed-bean-class>arduino.jsf.beans.RequestData</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>applicationData</property-name>
<value>#{applicationData}</value>
</managed-property>
</managed-bean>
</faces-config>
- lignes 12-18 : définissent le fichier des messages internationalisés. Il s'appelle [messages.properties] et se trouve dans le Classpath du projet [1] :

- ligne 19 : un kit de rendu des pages XHTML. Ici, c'est le kit de Primefaces Mobile qui est utilisé ;
- lignes 23-27 : le bean de portée [Application][2] ;
- lignes 29-33 : le bean de portée [Session] [2] ;
- lignes 35-43 : le bean de portée [Request] [2]. On notera qu'on y injecte la référence du bean de portée [Application] (lignes 39-42).
XII-E-6. Le bean de portée [Application]▲
Le bean de portée [Application] est instancié au démarrage de l'application JSF. Il reste en mémoire pendant la durée de vie de l'application d'où son nom. Il est accessible à toutes les requêtes de tous les utilisateurs. A cause de cette concurrence d'accès, il est généralement utilisé uniquement en lecture.
Le code de la classe [ApplicationData] est le suivant :
Sélectionnez 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.
package arduino.jsf.beans;
...
public class ApplicationData implements IMetier, Serializable {
private static final long serialVersionUID = 1L;
private IMetier metier;
@PostConstruct
public void init() {
metier = (IMetier) new ClassPathXmlApplicationContext("spring-restClient.xml").getBean("metier");
}
@Override
public Collection<Arduino> getArduinos() {
return metier.getArduinos();
}
@Override
public Reponse pinRead(String idCommande, String idArduino, int pin, String mode) {
return metier.pinRead(idCommande, idArduino, pin, mode);
}
@Override
public Reponse pinWrite(String idCommande, String idArduino, int pin, String mode, int val) {
return metier.pinWrite(idCommande, idArduino, pin, mode, val);
}
@Override
public Reponse faireClignoterLed(String idCommande, String idArduino, int pin, int millis, int nbIter) {
return metier.faireClignoterLed(idCommande, idArduino, pin, millis, nbIter);
}
@Override
public List<String> sendCommandesJson(String idArduino, List<String> commandes) {
return metier.sendCommandesJson(idArduino, commandes);
}
@Override
public List<Reponse> sendCommandes(String idArduino, List<Commande> commandes) {
return metier.sendCommandes(idArduino, commandes);
}
}
- ligne 5 : la classe [ApplicationData] implémente l'interface [IMetier] du client REST ;
- ligne 9 : une référence sur le client REST ;
- lignes 13-16 : la référence sur le client REST est obtenue avec Spring ;
- lignes 18-46 : implémentation de l'interface [IMetier]. Tout est délégué au client REST. Nous allons ainsi pouvoir cacher le client REST aux autres beans de l'application JSF. Ceux-ci s'adresseront au bean [ApplicationData] plutôt qu'au client REST.
XII-E-7. Le fichier de configuration de Spring▲
Le fichier [spring-restClient.xml] est le même que celui décrit au paragraphe , page .
XII-E-8. Le bean de portée Session▲
Le bean de portée [Session] est la mémoire d'un utilisateur. Toutes les requêtes de celui-ci peuvent écrire et lire de l'information dans cette mémoire. Ici, nous ne mémorisons qu'une information :
Sélectionnez 1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
package arduino.jsf.beans;
public class SessionData {
String locale="fr";
...
}
- ligne 6 : l'unique information de la session : la langue utilisée pour les pages affichées par l'application web mobile, ici le français.
XII-E-9. La page principale du projet PFM (PrimeFaces Mobile)▲
La page [index.html] ci-dessus est l'unique page du projet PFM. Son code est le suivant :
Sélectionnez 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.
<f:view xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:pm="http://primefaces.org/mobile"
contentType="text/html"
locale="#{sessionData.locale}">
<pm:page title="#{msg['appli.titre']}">
<pm:view id="home">
<ui:include src="home.xhtml"/>
</pm:view>
<pm:view id="commands">
<ui:include src="commands.xhtml"/>
</pm:view>
<pm:view id="blink">
<ui:include src="blink.xhtml"/>
</pm:view>
<pm:view id="pinWrite">
<ui:include src="pinWrite.xhtml"/>
</pm:view>
<pm:view id="pinRead">
<ui:include src="pinRead.xhtml"/>
</pm:view>
</pm:page>
</f:view>
- la page [index.html] est l'unique page affichée par l'application. Elle définit cinq vues. A un moment donné, une seule de ces cinq vues est visible. Au démarrage c'est la première qui est visible. On passe ensuite aux suivantes par navigation ;
- lignes 11-13 : la vue [home]. Elle présente quatre liens pour passer à l'une des quatre autres vues. Ces dernières ont toutes une icône permettant de revenir à la vue [home] ;
- lignes 14-16 : la vue [commands] permet d'envoyer une commande JSON aux Arduinos connectés ;
- lignes 17-19 : la vue [blink] permet de faire clignoter une led sur les Arduinos connectés ;
- lignes 20-22 : la vue [pinWrite] permet d'écrire une valeur binaire ou analogique sur l'une des pins des Arduinos connectés ;
- lignes 23-25 : la vue [pinRead] permet de lire la valeur binaire ou analogique de l'une des pins des Arduinos connectés.
XII-E-10. La vue [home]▲
Le code [home.xhtml] de cette vue est le suivant :
Sélectionnez 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.
<?xml version='1.0' encoding='UTF-8' ?>
<!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:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:pm="http://primefaces.org/mobile"
xmlns:ui="http://java.sun.com/jsf/facelets">
<pm:header title="#{msg['appli.titre']}" swatch="b">
<f:facet name="left">
<p:button icon="gear" value=" " href="#config" />
</f:facet>
</pm:header>
<pm:content>
<h:form id="frmHome">
<p:dataList id="list1" type="inset">
<f:facet name="header">#{msg['menu.options']}</f:facet>
<h:outputLink value="#pinRead">#{msg['menu.pinRead']}</h:outputLink>
<h:outputLink value="#pinWrite">#{msg['menu.pinWrite']}</h:outputLink>
<h:outputLink value="#blink">#{msg['menu.clignoter']}</h:outputLink>
<h:outputLink value="#commands">#{msg['menu.commande']}</h:outputLink>
</p:dataList>
</h:form>
</pm:content>
<pm:footer>
<div align="center">
© Votre nom<br/>ISTIA, université d'Angers
</div>
</pm:footer>
</html>
- lignes 11-15 : définissent l'entête de la vue ;
- ligne 11 : le libellé [1] de l'entête ;
- ligne 13 : le bouton [2]. Un clic sur ce bouton fait apparaître la vue [config.xhtml] (attribut href). Ici, elle n'a pas été définie. Donc le bouton sera inactif. Vous pourrez le rendre actif en créant une vue [config.xhtml] permettant de choisir une autre langue que le français ;
- ligne 16 : le contenu de la vue. La balise est obligatoire ;
- ligne 17 : le formulaire. Il y en normalement un dans chaque vue ;
- ligne 18 : l'unique composant de ce formulaire, un [DataList] ;
- ligne 19 : le libellé [3] du DataList ;
- ligne 20 : un lien vers la vue [pinRead] (attribut value) ;
- ligne 21 : un lien vers la vue [pinWrite] (attribut value) ;
- ligne 22 : un lien vers la vue [blink] (attribut value) ;
- ligne 23 : un lien vers la vue [commands] (attribut value) ;
- ligne 29 : le bas de page de la vue affichée. Mettez votre nom par exemple.
XII-E-11. Le fichier des messages▲
La vue [home.xhtml] détaillée précédemment utilise des messages provenant du fichier [messages_fr.properties] :
Sélectionnez 1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
appli.titre=Mobilit\u00e9 & Domotique
appli.footer=
menu.options=Options
menu.pinWrite=| Ecrire une valeur sur une pin
menu.pinRead=| Lire la valeur d'une pin
menu.clignoter=| Faire clignoter une led
menu.commande=| Envoyer une commande Json
menu.rafraichir=Rafra\u00eechir la liste
cmd.executer=Ex\u00e9cuter
cmd.rafraichir=Rafra\u00eechir
...
Si vous internationalisez l'application en y ajoutant l'anglais, vous aurez à remplir le fichier [messages_en.properties] avec les mêmes clés que dans [messages_fr.properties] et des textes en anglais.
XII-E-12. La vue [blink.xhtml]▲
Nous allons examiner l'une des vues comme exemple à suivre. La vue [blink.xhtml] est la suivante :
Le code XHTML est le suivant :
Sélectionnez 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.
<?xml version='1.0' encoding='UTF-8' ?>
<!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:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:pm="http://primefaces.org/mobile"
xmlns:ui="http://java.sun.com/jsf/facelets">
<pm:header title="#{msg['appli.titre']}" swatch="b">
<f:facet name="left">
<p:button icon="home" value=" " href="#home" />
</f:facet>
</pm:header>
<pm:content>
<h:form id="frmBlink">
<h2>#{msg['listeArduinos']}</h2>
<h:selectManyCheckbox id="arduinos" value="#{requestData.selectedArduinosIds}">
<f:selectItems var="arduino" value="#{applicationData.arduinos}" itemLabel="#{arduino.ip}" itemValue="#{arduino.id}"/>
</h:selectManyCheckbox>
<br/>
<p:commandButton id="cmdRafraichir" value="#{msg['menu.rafraichir']}" immediate="true" update=":frmBlink:arduinos"/>
<h2>#{msg['commande']}</h2>
<pm:field>
<h:outputLabel for="pin" value="#{msg['pinNumber']}"/>
<h:selectOneMenu id="pin" value="#{requestData.pin}">
<f:selectItems var="pin" value="#{requestData.pins}" itemLabel="#{pin}" itemValue="#{pin}"/>
</h:selectOneMenu>
</pm:field>
<h:outputText id="msgErreur1" value="#{msg[requestData.pinMessageKey]}" style="color: red"/>
<pm:field>
<h:outputLabel for="millis" value="#{msg['clignoter.dureeClignotement']}"/>
<h:inputText id="millis" value="#{requestData.dureeClignotement}"
required="true" requiredMessage="#{msg['clignoter.dureeClignotement.required']}"
validatorMessage="#{msg['clignoter.dureeClignotement.error']}">
<f:validateLongRange minimum="100" maximum="10000"/>
</h:inputText>
</pm:field>
<h:message id="msgErreur2" for="millis" style="color: red"/>
<pm:field>
<h:outputLabel for="nbClignotements" value="#{msg['clignoter.nbClignotements']}"/>
<h:inputText id="nbClignotements" value="#{requestData.nbClignotements}"
required="true" requiredMessage="#{msg['clignoter.nbClignotements.required']}"
validatorMessage="#{msg['clignoter.nbClignotements.error']}">
<f:validateLongRange minimum="1" maximum="100"/>
</h:inputText>
</pm:field>
<h:message id="msgErreur3" for="nbClignotements" style="color: red"/>
<p:commandButton id="clignoter" value="#{msg['cmd.executer']}" action="#{requestData.clignoter}" update=":frmBlink:msgErreur1 :frmBlink:msgErreur2 :frmBlink:msgErreur3 :frmBlink:reponses :frmBlink:exceptions"/>
<p:outputPanel id="reponses">
<ui:fragment rendered="#{! empty requestData.réponses}">
<hr/>
<h2>#{msg['réponsesArduinos']}</h2>
<ul>
<ui:repeat var="réponse" value="#{requestData.réponses}">
<li>#{réponse}</li>
</ui:repeat>
</ul>
</ui:fragment>
</p:outputPanel>
<p:outputPanel id="exceptions">
<ui:fragment rendered="#{! empty requestData.exceptions}">
<hr/>
<h2>#{msg['exceptions']}</h2>
<ul>
<ui:repeat var="exception" value="#{requestData.exceptions}">
<li>#{exception}</li>
</ui:repeat>
</ul>
</ui:fragment>
</p:outputPanel>
</h:form>
</pm:content>
<pm:footer>
<div align="center">
© Votre nom<br/>ISTIA, université d'Angers
</div>
</pm:footer>
</html>
- lignes 10-14 : l'entête [1] de la vue ;
- ligne 12 : l'icône [A] qui permet de revenir sur la vue [home] (attribut href) ;
- ligne 15 : le contenu de la vue ;
- ligne 16 : le formulaire de la vue ;
- ligne 18 : le libellé [2] ;
- ligne 19-21 : la liste de cases à cocher [3] ;
- ligne 19 : la liste des éléments cochés sera mémorisée dans le champ [selectedArduinosIds] du bean de portée [request] RequestData :
Sélectionnez 1.
private String[] selectedArduinosIds;
- ligne 20 : la liste est alimentée par la liste des Arduinos connectés. Celle-ci est trouvée dans le bean [ApplicationData] :
Sélectionnez 1.
2.
3.
4.
@Override
public Collection<Arduino> getArduinos() {
return metier.getArduinos();
}
Le libellé de chaque case à cocher sera l'adresse IP de l'Arduino (attribut itemLabel). La valeur postée sera l'identifiant de l'Arduino (attribut itemValue) ;
- ligne 24 : le bouton [4] qui demande au client REST, la liste des Arduinos connectés. Elle le fait avec un appel Ajax, donc sans rechargement de page. L'attribut immediate=true permet de s'affranchir des tests de validité du formulaire. Ils ne seront pas exécutés. La liste des Arduinos de la ligne 19 sera régénérée (attribut update) ;
- ligne 25 : le libellé [5] ;
- ligne 27 : un champ PFM ;
- ligne 28 : le libellé [6]. Notez l'attribut for qui référence le composant d'id pin. PFM utilise cette information pour sa mise en page ;
- lignes 29-31 : la liste déroulante [7]. La valeur postée sera affectée (attribut value) au champ suivant de [RequestData] :
Sélectionnez
- ligne 30 : la liste déroulante est alimentée (attribut value) par le champ suivant de [RequestData] :
Sélectionnez 1.
private String[] pins = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13" };
Le libellé affiché (itemLabel) ainsi que la valeur postée (itemValue) seront le n° de pin.
- ligne 33 : un message d'erreur non représenté sur la copie d'écran. Ce message apparaît si le n° de pin posté est invalide ;
- lignes 35-42 : le champ PFM des composants 8 et 9 ;
- ligne 36 : le libellé [8] ;
-
ligne 37 : le champ de saisie [9]. La valeur postée sera mémorisée (attribut value) dans le champ suivant de [RequestData] :
On peut se demander si un type int n'aurait pas été préférable. A l'affichage, la valeur initiale de [dureeClignotement] est affichée dans le champ de saisie. Si le type est int, la valeur 0 est affichée. Avec le type String, rien n'est affiché. D'où le choix de ce dernier ;
Sélectionnez 1.
private String dureeClignotement;
-
ligne 38 : le champ est obligatoire (attribut required) ;
-
ligne 40 : la durée du clignotement doit être entre 100 ms et 10 s ;
-
ligne 43 : affiche un message d'erreur si la saisie est incorrecte ;
-
lignes 45-53 : on a un code analogue pour la saisie du nombre de clignotements ;
- lignes 54 : le bouton qui va faire clignoter les leds des Arduinos sélectionnés ;
La méthode [RequestData].clignoter va être exécutée (attribut action) par un appel Ajax. Les zones identifiées par msgErreur1 (ligne 33), msgErreur2 (ligne 43), msgErreur3 (ligne 53), reponses (ligne 56), exceptions (ligne 69) seront mises à jour avec le résultat de l'appel Ajax (attribut update) ;
- lignes 56-67 : un bloc de la vue qui contient le fragment de la ligne 57. Celui-ci n'est affiché que si après l'appel Ajax, la liste des réponses des Arduinos est non vide ;

- ligne 60 : le libellé [1] ;
-
lignes 62-64 : ce code produit la liste [2] alimentée par le champ suivant de [RequestData] :
Sélectionnez 1.
private List<String> réponses;
-
ligne 63 : la réponse d'un Arduino est affichée sous la forme [2] ;
-
lignes 69-80 : un bloc de la vue qui contient le fragment de la ligne 70. Celui-ci n'est affiché que si après l'appel Ajax, la liste des exceptions produite par l'appel Ajax est non vide ;
-
ligne 73 : le libellé [1] ;
-
lignes 74-78 : ce code produit la liste [2] alimentée par le champ suivant de [RequestData] :
Sélectionnez 1.
private List<String> exceptions;
- ligne 76 : chaque exception est affichée sous la forme [2]. Ici le câble réseau des Arduinos avait été déconnecté de l'ordinateur hôte ;
La vue [blink.xhtml] utilise les messages suivants du fichier [messages_fr.properties] :
Sélectionnez 1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
appli.titre=Mobilit\u00e9 & Domotique
appli.footer=
...............
cmd.clignoter=Faire clignoter
clignoter.nbClignotements=Nombre de clignotements
clignoter.dureeClignotement=Dur\u00e9e du clignotement en millisecondes
clignoter.dureeClignotement.required=Donn\u00e9e requise
clignoter.nbClignotements.required=Donn\u00e9e requise
clignoter.dureeClignotement.error=La dur\u00e9e doit \u00eatre comprise entre 100 et 10000 millisecondes
clignoter.nbClignotements.error=Le nombre de clignotements doit \u00eatre compris entre 1 et 100
.............
exceptions=Il y a eu des exceptions
Pour terminer cet exemple, il ne nous reste plus qu'à décrire la méthode [RequestData].clignoter qui fait clignoter les leds.
XII-E-13. La méthode [RequestData].clignoter▲
Le code de la méthode est le suivant :
Sélectionnez 1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
public String clignoter() throws IOException {
réponses = new ArrayList<String>();
exceptions = new ArrayList<String>();
for (String arduinoId : selectedArduinosIds) {
try {
réponses.add(applicationData.faireClignoterLed(arduinoId, arduinoId, pin, Integer.parseInt(dureeClignotement),
Integer.parseInt(nbClignotements)).toString());
} catch (Exception ex) {
recordException(ex);
}
}
return null;
}
- pour comprendre ce code, il faut se souvenir que les champs [selectedArduinosIds, dureeClignotement, nbClignotements] ont reçu les valeurs saisies par l'utilisateur ;
- ligne 7 : on parcourt la liste des Arduinos sélectionnés par l'utilisateur ;
- ligne 10 : pour chacun d'eux, on demande au bean [ApplicationData] de faire clignoter la led. Les réponses JSON des Arduinos sont cumulés dans la liste de la ligne 4 ;
- ligne 14 : les exceptions sont cumulées dans la liste de la ligne 5 par la méthode privée [recordException] suivante :
Sélectionnez 1.
2.
3.
4.
5.
6.
7.
8.
9.
private void recordException(Exception ex) {
Throwable th = ex;
while (th != null) {
exceptions.add(th.getMessage());
th = th.getCause();
}
}
: en suivant le modèle qui vient d'être décrit pour la vue [blink], réalisez les vues [pinWrite], [pinRead] et [commands].