Optimisation de la vitesse d’affichage de la liste des étudiants

Au départ, les tests effectués pour l’affichage de la liste des étudiants inscrits à une étape ou un élément pédagogique étaient fait sur une promotion d’une dizaine d’étudiants et il n’y avait aucun problème notable. Cependant, après avoir testé une promotion de 240 étudiants (et augmenté le timeout d’une portlet dans uportal) il s’est avéré que le laps de temps écoulé entre la validation du formulaire contenant le code étape et la visualisation de la liste sur la page suivante pouvait attendre 10 secondes, ce qui est beaucoup trop long pour prétendre à un certain confort de navigation. Il fallait donc résoudre ce problème. Nous avons procédé en trois étapes.

 

1/ gain de temps sur les requêtes SQL :

Précédemment, lorsqu’un utilisateur validait sa saisie, on récupérait la liste de tous les étudiants appartenant à cette étape (ou cet élément pédagogique), puis pour chacun d’eux on récupérait, avec une autre requête, son login pour constituer son adresse mail. On avait donc une requête pour chaque étudiant, et pour une ‘promotion’ de 200 étudiants, le temps d’exécution des 200 requêtes pouvait atteindre les 5secondes. Il fallait donc régler ce premier problème. Pour cela nous avons ajouté la récupération du login dans la requête principale qui récupère les étudiants d’une étape (ou d’un élément pédagogique) et les informations les concernant. Par exemple (de manière très simplifiée) une requête du type :

Select nom, prenom, adresse, date_naissance

From etudiant, etape

Where etudiant.code=etape.cod_etu

Devient

Select nom, prenom, adresse, date_naissance, login

From etudiant, etape, llogin

Where etudiant.code=etape.cod_etu

And llogin.cod_etu=etudiant.code

Nous avons ainsi gagné près de 5 secondes.

 

2/ Substitution des tags <c :forEach

Après la première modification, qui nous faisait gagnée un certain temps, il s’est avéré que l’affichage lui-même, c’est-à-dire après la redirection sur la page xhtml concernée, pouvait durer jusqu’à 5 secondes pour 240 étudiants. Ce qui était d’autant plus gênant que, contrairement l’attente due aux requêtes SQL, ce laps de temps était inévitable ; on avait 5 secondes d’attente après avoir fait la touche de retour depuis le dossier d’un étudiant ou depuis le trombinoscope par exemple.

Ceci était du au tag jstl ‘c :forEach’ que nous utilisions pour « rendre » la liste d’étudiant dans un tableau html. Nous l’avons remplacé par le tag tomahawk ‘<t:dataTable’ après avoir fait les modifications nécessaires (ajout du fichier tomahawk.taglib.xml et du paramètre facelets.LIBRARIES dans le web.xml).

 

3/ ajout d’un ‘dataScroller’ :

Suite à ces deux modifications, l’affichage des 240 étudiants durait approximativement 2 secondes. Nous avons donc décidé d’ajouter un dataScroller dans le footer de la datatable permettant ainsi d’afficher les étudiants par lots de 15, optimisant ainsi l’affichage de la page. Nous comptons prochainement rendre l’utilisation du Scroller paramétrage dans le fichier monDossierWeb.xml (utilisation facultative et ajustement du nombre d’étudiants affichés par page).

Authentification de l’utilisateur

Nous avons vu dans l’article précédent comment se configuré le bean ‘security’. Nous allons maintenant voir comment il est utilisé par le ‘SessionController’ lors de l’authentification de l’utilisateur.

Nous avons déjà vu dans un des posts précédents intitulé « Gestion des rôles avec on-load et CAS » que le composant jsf ‘on-load’ sécurisait la page d’accueil de l’application en redirigeant automatiquement vers la méthode ‘accueil()’ du ‘SessionController’. Celle-ci à pour mission d’identifier l’utilisateur et de le rediriger vers la bonne page d’accueil (dans stylesheets/ens ou stylesheets/etu) ou vers une page d’erreur en cas de problème. Voici le fonctionnement de la méthode ‘accueil()’ :

/**
* @return la page d’accueil selon le type de l’utilisateur
*/

public String accueil(){
typeUser();
if(typeuser.equals(« etudiant »)){
return « navigationIndexEtu »;
} else if(typeuser.equals(« enseignant »)){
return « navigationIndexEns »;
}else if (typeuser.equals(« non-autorise »)){
return « error »;
}else{
return « error-connexion »;
}
}

‘accueil()’ utilise la méthode typeUser(), qui se sert de la méthode ‘getTypeUser’ du bean ‘security’ :

/**

* Détermine le type de l’utilisateur
*/
public void typeUser(){
typeuser= » »;
User user=getCurrentUser();
if(user==null){
//utitisateur non loggué
typeuser= »nonloggue »;
}else{
setIduser(user.getId());
typeuser=security.getTypeUser(getIduser());
if(typeuser.equals(« etudiant »)){
CodeIndDossier=service.getCodInd(getIduser());
//génération du ticket pour sa photo
photo.init(CodeIndDossier);
}
if(typeuser.equals(« enseignant »)){
//génération du ticket pour toutes les photos
photo.init();
}}
}


Beans de configuration

Dans le canal CMonDossierWeb d’origine, certaines parties sont configurables par l’intermédiaire du fichier CMonDossierWeb.xml : extension des mails etudiant (@univ-nancy2.fr par exemple), utilisation du canal MailTo ou d’un lien ‘mailto :’ pour l’envoi des mail, affichage du calendrier de rentrée etc.

Toutes ces possibilités doivent être présentes dans esup-mondossierweb, c’est pourquoi nous avons créé un fichier de configuration monDossierWeb.xml placé dans properties/monDossierWeb. Ce fichier contient la définition de 2 beans utiles à l’application et configurables : ‘config’ et ‘security’. De cette manière nous n’avons par la suite qu’à injecter le bean utile dans ceux ayant besoin de leurs paramètres ou de leurs méthodes.

Nous avons bien sur du rajouter l’import :

<import resource= »monDossierWeb/monDossierWeb.xml » />

Dans le fichier properties/applicationContext.xml.

 

 

1/ Le bean ‘config’ :

Il sert simplement à stocker dans un bean les paramètres de base de l’application. Il ne propose aucune méthode (à part ses setters et getters bien sur).

<bean id= »config » class= »org.esupportail.mondossierweb.domain.beans.Config »>

<description>un bean pour stocker configuration de l’application.</description>

 

<property name= »extMail »>

<description>l’extension au login pour l’e-mail des étudiants</description>

<value>@univ-nancy2.fr</value>

</property>

 

<property name= »lienContact »>

<description>l’e-mail du lien ‘nous contacter'</description>

<value>scolarite@univ-nancy2.fr</value>

</property>

 

<property name= »mailInstalle »>

<description>vrai si la possibilité d’appeler le canal MailTo est implémentée et que l’on souhaite l’en servir pour l’envoi de mail. Sinon l’application utilise un simple lien ‘mailto:'</description>

<value>false</value>

</property>

 

<property name= »msgConnexion »>

<description>le message d’erreur de connection</description>

<value>Vous n’êtes pas connecté</value>

</property>

 

<property name= »msgRefusAcces »>

<description>le message d’interdiction d’acces</description>

<value>Vous n’avez pas accès à cette partie.</value>

</property>

 

<property name= »calExam »>

<description>utiliser calExam a la place de Calendrier original</description>

<value>false</value>

</property>

 

<property name= »calRent »>

<description>utiliser le calendrier de rentrée</description>

<value>false</value>

</property>

 

<property name= »resultats »>

<description>utiliser Resultats a la place de Notes original</description>

<value>false</value>

</property>

 

</bean>

 

 

‘config’ est par exemple utilisé dans le fichier template-contact.xml pour afficher un simple lien ‘mailto :’ et récupérer l’adresse de la scolarité pour le lien ‘nous contacter’ :

<h:outputText rendered= »#{!config.mailInstalle} »>

<a href= »mailto:#{config.lienContact} »><b><u>Nous contacter</u></b></a>

</h:outputText>

 

Il est, par exemple, aussi utilisé par le converter ‘EmailConverter’ pour donner l’adresse mail d’un étudiant à partir de son login :

public String getMail(String login) {

if (config.getExtMail() != null && !config.getExtMail().equals(«  »))

return login + config.getExtMail();

return «  »;

}

 

 

2/ Le bean ‘security’ :

Il sert à configurer l’accès à l’application. On y renseigne le type ldap correspondant à l’étudiant (si ldap renvoi ce type pour le login de l’utilisateur, il est étudiant, sinon on procède à d’autres vérifications), et la liste des groupes uportal dont les membres sont autorisés à accéder à l’application en tant qu’enseignant.

 

<bean id= »security » class= »org.esupportail.mondossierweb.domain.beans.Security »>

<description>

bean pour stocker la configuration de l’accès à l’application.

</description>

 

<property name= »service » ref= »service »>

<description>Le service d’accès à la base de donnée</description>

</property>

 

<property name= »ldapService » ref= »ldapService »>

<description>le service ldap</description>

</property>

 

<property name= »portalService » ref= »portalService »>

<description>le service uportal</description>

</property>

 

<property name= »typeEtudiantLdap »>

<description>Le type ldap designant un etudiant</description>

<value>student</value>

</property>

 

<property name= »listeGroupesPortal »>

<description>La liste des groupe uportal autorisant l’accès à mondossierWeb en tant qu’enseignant</description>

<list>

<value>Tous les groupes de personnes</value>

</list>

</property>

 

</bean>

 

Il fournit aussi des méthodes indispensables à l’identification de l’utilisateur. En effet, ‘Security.java’ implémente l’interface ‘ISecurity’, qui définit les méthodes utilisées par le SessionController lors de l’identification de l’utilisateur. Voici le code de l’interface ‘ISecutity’ :

public interface ISecurity {

 

//donne le type de l’utilisateur dans l’application a partir de son identifiant

public String getTypeUser(String id);

 

//donne le type ldap de l’utilisateur a partir de son identifiant

public String typeLdap(String login);

}

 

Et voici un exemple de notre implémentation des méthodes de ‘ISecurity’ dans ‘Security’ à l’heure actuelle:

/**

* @param login de l’utilisateur

* @return le type retourné par ldap.

*/

public String typeLdap(String login){

try{

LdapUser ldapuser=ldapService.getLdapUser(login);

Map mattributs =ldapuser.getAttributes();

List<String> ltype=(List<String>)mattributs.get(« edupersonprimaryaffiliation »);

return ltype.get(0);

}catch(Exception e){

return « error »;

}

}

 

 

 

 

/**

* @param id identifiant de l’utilisateur

* @return type utilisateur (etudiant, enseignant ou non-autorise)

*/

public String getTypeUser(String id){

String typeuser= » »;

String type=typeLdap(id);

if(type.equals(getTypeEtudiantLdap())){

typeuser= »etudiant »;

}else{

//on cherche a savoir si l’employé a acces (ex: c’est un enseignant)

//si autorise type=enseignant, sinon type=non-autorise

try{

//recupère l’utilisateur uportal

PortalUser portaluser = portalService.getUser(id);

//on cherche si il appartient a un groupe

boolean useruportal=false;

//on reucpère la liste de groupe mis dans le bean security

List<String> listegroupes=getListeGroupesPortal();

//on regarde si il appartient a un des groupes

for(String nomgroupe : listegroupes){

//on cherche le groupe

PortalGroup pgroup=portalService.getGroupByName(nomgroupe); if(pgroup!=null){

//on regarde si l’utilisateur appartient a ce groupe if(portalService.isUserMemberOfGroup(portaluser, pgroup)){

//c’est un utilisateur uportal

useruportal=true;

}

}

if(useruportal){

//c’est un utilisateur uportal il est autorisé en tant qu’enseignant

typeuser= »enseignant »;

}else{

//Test présence dans la table utilisateur d’Apogee

//on regarde si il est dans la table utilisateur

String coduti=service.getCodUti(id);

if(coduti!=null){

typeuser= »enseignant »;

}else{

typeuser= »non-autorise »;

}

}

}catch(Exception e){

//erreur avec uportal

//Test présence dans la table utilisateur de Apogee

//on regarde si il est dans la table utilisateur

String coduti=service.getCodUti(id);

if(coduti!=null){

typeuser= »enseignant »;

}else{

typeuser= »non-autorise »;

}

}

}

return typeuser;

}

Publié dans Spring. Leave a Comment »

Ajout de ‘template-contact.xhtml’

Dans l’application CMonDossierWeb original un lien ‘nous contacter’ est affiché à la page d’accueil de CMonDossierWebEns et à droite du menu de CMonDossierWeb quand il n’est pas servant, c’est-à-dire quand l’utilisateur est un étudiant.

Pour réaliser cela, nous avons ajouté un template appelé template-contact.xhtml dans stylesheets/template. Pour l’instant il affiche juste ‘nous contacter’ car nous n’avons pas mis en place le lien vers le canal MailTo (si c’est faisable). Voici le template-contact.xhtml, très basique donc :

<!DOCTYPE html PUBLIC « -//W3C//DTD XHTML 1.0 Transitional//EN » « http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd »&gt;

<div xmlns:h= »http://java.sun.com/jsf/html &raquo;

xmlns:e= »http://commons.esup-portail.org &raquo;

xmlns:ui= »http://java.sun.com/jsf/facelets »&gt;

Nous contacter

</div>

 

 

 

Celui-ci est ensuite appelé par template-etu.xhtml (qui affichait jusque là les liens de retour pour l’utilisateur enseignant) qui l’affiche seulement si l’utilisateur est un étudiant :

<div xmlns:h= »http://java.sun.com/jsf/html &raquo;

xmlns:e= »http://commons.esup-portail.org &raquo;

xmlns:ui= »http://java.sun.com/jsf/facelets »&gt;

<ui:composition template= »/stylesheets/template/template.xhtml »>

<ui:define name= »icones »>

<h:form id= »formMenuRetour » rendered= »#{sessionController.enseignant} »>

<h:commandLink id= »linkretourliste » action= »navigationInscritsArborescence » rendered= »#{etatCivilController.vientDuneListe} »>

<img border=’0′ src=’back.gif’ alt=’Retour à la liste’ title=’Retour à la liste’ />

</h:commandLink>

<h:commandLink id= »linkretourtrombi » action= »navigationInscritsTrombinoscope » rendered= »#{etatCivilController.vientDuTrombi} »>

<img border=’0′ src=’back.gif’ alt=’Retour à la liste’ title=’Retour à la liste’ />

</h:commandLink>

<h:commandLink id= »linkretourrecherche » action= »navigationRechercheNumero » rendered= »#{etatCivilController.vientDuNumero} »>

<img border=’0′ src=’find.gif’ alt=’Nouvelle recherche’ title=’Nouvelle recherche’ />

</h:commandLink>

</h:form>

 

 

<h:outputText rendered= »#{!sessionController.enseignant} »>

<ui:include src= »/stylesheets/template/template-contact.xhtml »/>

</h:outputText>

 

 

</ui:define>

<ui:define name= »menu »>

<ui:include src= »/stylesheets/template/template-menu.xhtml »/>

</ui:define>

</ui:composition>

</div>

 

Template-contact.xhtml est aussi appelé par ens/welcome.xhtml. On ajoute juste :

<ui:define name= »icones »>

<ui:include src= »/stylesheets/template/template-contact.xhtml »/>

</ui:define>

 

Après :

<ui:composition template= »/stylesheets/template/template-ens.xhtml »>

<ui:define name= »titre »>

Accueil

</ui:define>

 

On retrouve ainsi notre lien sur la page d’accueil enseignant et à l’extrémité du menu étudiant si l’utilisateur est un étudiant.

Création des taches Ant : _toServlet et _toPortlet

MonDossierWeb est une application qui doit marcher aussi bien en portlet qu’en servlet. Ce qui impose que les tests lors du développement soient faits dans les deux contextes. Or le passage d’un contexte à l’autre nécessite la modification de 4 fichiers : properties/jsf/application.xml (modification du ViewHandler) ; stylesheets/template/template.xhtml ; web.xml et build.properties.

Toutes ces modifications s’avèrent fastidieuses quand on travail par exemple sur l’élaboration du système de téléchargement de fichier (voir post suivant) qui nécessite des tests dans les deux contextes et où on doit donc passer souvent de l’un à l’autre.

Deux taches Ant ont donc été rajoutées au build-devel.xml de notre application : ‘_toServlet’ et ‘_toPortlet’ qui permettent respectivement de passer le projet en portlet ou en servlet. Voici leur fonctionnement (à noter qu’elles s’appuient sur la tache ‘_deleteFileForContext’ également créé pour l’occasion) :

<target name= »_deleteFileForContext » description= »Make change for servlet use »>

<echo>Deleting in progress, do not interrupt this task!</echo>

<delete file= »./properties/jsf/application.xml »/>

<delete file= »./webapp/stylesheets/template/template.xhtml »/>

<delete file= »./webapp/WEB-INF/web.xml »/>

<delete file= »./build.properties »/>

</target>

 

 

<target name= »_toServlet » depends= »_deleteFileForContext » description= »Make change for servlet use »>

<echo>Copying in progress, do not interrupt this task!</echo>

<copy file= »./properties/jsf/application-servlet.xml » tofile= »./properties/jsf/application.xml » />

<copy file= »./webapp/stylesheets/template/template-servlet.xhtml » tofile= »./webapp/stylesheets/template/template.xhtml » />

<copy file= »./webapp/WEB-INF/web-servlet-example.xml » tofile= »./webapp/WEB-INF/web.xml » />

<copy file= »./build-servlet-example.properties » tofile= »./build.properties » />

<echo>Refactoring complete! Please refresh your project now</echo>

</target>

 

 

<target name= »_toPortlet » depends= »_deleteFileForContext » description= »Make change for portlet use »>

<echo>Copying in progress, do not interrupt this task!</echo>

<copy file= »./properties/jsf/application-portlet.xml » tofile= »./properties/jsf/application.xml » />

<copy file= »./webapp/stylesheets/template/template-portlet.xhtml » tofile= »./webapp/stylesheets/template/template.xhtml » />

<copy file= »./webapp/WEB-INF/web-portlet-example.xml » tofile= »./webapp/WEB-INF/web.xml » />

<copy file= »./build-portlet-example.properties » tofile= »./build.properties » />

<echo>Refactoring complete! Please refresh your project now</echo>

</target>

Téléchargement de fichiers (servlet et portlet)

MonDossierWeb doit proposé de télécharger des fichier excel et pdf : trombinoscope d’une liste d’étudiants en pdf, une liste d’inscrits en excel, etc.

Ces fichiers doivent être générés ( par un ‘controller’ par exemple) au moment du téléchargement.

Au départ nous avions réussi à faire un téléchargement pour le fonctionnement en servlet. Il fonctionnait de la manière suivante :

//récupération de l’objet ‘HttpServletResponse’

FacesContext context = FacesContext.getCurrentInstance();

ExternalContext external = context.getExternalContext();

HttpServletResponse response = (HttpServletResponse) external.getResponse();

//formatage de la réponse

response.setContentType(« application/octet-stream »);

response.setHeader (« Content-Disposition », « attachment; filename=\ »test.xls\ » » );

ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);

//création du fichier excel

HSSFWorkbook wb=creerExcel();

// Ecriture du fichier dans l’outputstream de la réponse

ServletOutputStream out;

try {

out = response.getOutputStream();

wb.write(baos);

baos.writeTo(out);

baos.flush();

//on ‘courcircuite’ le cycle de vie JSF :

context.responseComplete();

} catch (IOException e) {

e.printStackTrace();

}

Evidemment cette méthode de fonctionne pas en portlet car on ne peut récupérer l’objet ‘HttpServletResponse’. Il a donc fallu trouver une autre solution.

Il s’avère en fait que le téléchargement de fichiers placés dans la ‘response’ est impossible en portlet. La solution est donc de passer par une servlet. Nous avons l’avantage d’utiliser esup-commons, qui propose une telle servlet : « DownloadServlet ». Voici comment cela fonctionne en portlet :

//récupération de la session :

FacesContext context = FacesContext.getCurrentInstance();

ExternalContext external = context.getExternalContext();

ActionRequest request = (ActionRequest) external.getRequest();

PortletSession sessionp = request.getPortletSession();

try {

ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);

//création du fichier excel:

HSSFWorkbook wb=creerExcel();

//récupération du fichier en tableau de bytes

wb.write(baos);

byte[] bytes=baos.toByteArray();

//on place le tableau de bytes dans la session :

sessionp.setAttribute(« downloadData », bytes, PortletSession.APPLICATION_SCOPE);

//on renseigne le ‘contenttype’ dans la session :

sessionp.setAttribute(« downloadContentType », « application/octet-stream », PortletSession.APPLICATION_SCOPE);

//on place le nom du fichier dans la session :

sessionp.setAttribute(« downloadFilename », « test.xls », PortletSession.APPLICATION_SCOPE);

//on redirige vers la servlet:

external.redirect(external.getRequestContextPath() + « /download »);

} catch (DownloadException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

//on ‘courcircuite’ le cycle de vie JSF :

context.responseComplete();

Evidemment il faut auparavant déclarer la servlet dans le web.xml :

<servlet>

<servlet-name>Download Servlet</servlet-name>

<servlet-class>

org.esupportail.commons.web.servlet.DownloadServlet

</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>Download Servlet</servlet-name>

<url-pattern>/download</url-pattern>

</servlet-mapping>

Voici maintenant le fonctionnement de la servlet :

public void service(final ServletRequest servletRequest,

final ServletResponse servletResponse) throws ServletException {

HttpServletRequest request = (HttpServletRequest) servletRequest;

HttpServletResponse response = (HttpServletResponse) servletResponse;

try {

//récupération du contenttype

String contentType = (String) HttpUtils.getSessionAttribute(request, downloadContentType);

if (contentType != null) {

//formatage de la réponse

response.setContentType(contentType);

}

//recuperation du nom du fichier

String filename = (String) HttpUtils.getSessionAttribute(request, downloadFilename);

if (filename != null) {

//formatage de la réponse

response.setHeader(« Content-disposition », « inline; filename=\ » » + filename + « \ » »);

}

//récupération du fichier sous forme d’un tableau de bytes

byte [] data = (byte []) HttpUtils.getSessionAttribute(request, downloadData);

if (data == null) {

throw new DownloadException(« data is null, can not download »);

}

// Ecriture du fichier dans l’outputstream de la réponse

response.setContentLength(data.length);

ServletOutputStream out = response.getOutputStream();

out.write(data);

} catch (Exception e) {

Exception de = new DownloadException(e);

ExceptionUtils.catchException(getServletContext(), de, request);

throw new ServletException(de);

}

}

Vous remarquerez que cette servlet utilise la méthode getSessionAttribute(HttpServletRequest request, String name)

de la classe HttpUtils. Voici son fonctionnement :

public static Object getSessionDownloadAttribute(final HttpServletRequest request,

final String name) {


HttpSession session = request.getSession();
if (session == null) {
LOG.warn(« no session, can not get session attribute [ » + name + « ] »);
return null;
}

return session.getAttribute(name);

}

En fait, cette méthode va aller chercher l’attribut de session souhaité dans la request placée en paramètre.

Nous avons donc utilisé cette technique pour les deux types de contexte. Ainsi, le code décrit en début de Post pour le fonctionnement servlet devient :

//on récupère la session :

FacesContext context = FacesContext.getCurrentInstance();

ExternalContext external = context.getExternalContext();

HttpServletRequest request = (HttpServletRequest) external.getRequest();

HttpSession sessions = request.getSession();

try {

ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);

//creation du fichier:

HSSFWorkbook wb=creerExcel();

//récupération du fichier en tableau de bytes

wb.write(baos);

byte[] bytes=baos.toByteArray();

//on place les informations nécessaire et le fichier en session:

sessions.setAttribute(« downloadData », bytes);

sessions.setAttribute(« downloadContentType », « application/octet-stream »);

sessions.setAttribute(« downloadFilename », « test.xls »);

//redirection vers la servlet:

external.redirect(external.getRequestContextPath() + « /download »);

} catch (DownloadException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

//on ‘courcircuite’ le cycle de vie JSF :

context.responseComplete();

Et voila. Il ne reste qu’à déterminer le ‘context’ (servlet ou portlet) au moment du téléchargement pour exécuter le bon bout de code.

ATTENTION : pour que cela fonctionne en portlet il faut impérativement penser à indiquer :

emptySessionPath= »true » dans la déclaration du ‘Connector’ dans le server.xml de Tomcat. Ex:


<Connector port= »8080″ maxHttpHeaderSize= »8192″
maxThreads= »150″ minSpareThreads= »25″ maxSpareThreads= »75″
enableLookups= »false » redirectPort= »8443″ acceptCount= »100″
connectionTimeout= »20000″ disableUploadTimeout= »true »
emptySessionPath= »true » />

Sans cela la portlet et la servlet auront une Session différente et le code ne fonctionnera, générant probablement une erreur du type « can not download data is null » (voir code de la DownloadServlet) ou « prepare method was not called »

Gestion des rôles avec on-load et CAS

Esup_mondossierweb gère deux types d’utilisateur (étudiant et enseignant). Nous allons expliquer comment nous gérons l’accès aux différentes parties de l’application grâce à on-load et CAS.

La page d’accueil par défaut de l’application est stylesheets/welcome.xhtml. Elle est ‘protégée’ par le composant on-load qui redirige (via la méthode ‘accueil’ du SessionController) vers etu/welcome.xhtml ou ens/welcome.xhmtl suivant le type de l’utilisateur (voir le post ‘Redirection depuis l’accueil selon le type utilisateur’).

En plus de cela, on-load intercepte les accès aux pages se trouvant dans les répertoires etu/ et ens/ contenant respectivement les pages dédiées aux étudiants et aux enseignants.

Quand on tente d’accéder à une page de ens/ on-load lance la méthode isEns() du SessionController. Cette méthode retourne ‘null’ (on a accès à la page désirée) si l’utilisateur est un enseignant, ‘error’ si l’utilisateur est un étudiant, et ‘error-connexion’ si il n’est ni étudiant, ni enseignant donc non connecté.

’error’ renvoit vers la page error/acces-interdit.xhtml, qui affiche un message d’erreur et un lien vers la méthode ‘accueil’ du SessionController.

‘error-connexion’ revoit vers la page error/connexion.xhtml qui affiche un message d’erreur et un lien pour s’authentifier. Ce lien est en fait un lien vers la page ‘login.xhtml’ qui est ‘sécurisée’ par le filtre CAS ; l’utilisateur est donc automatiquement dirigé vers la page d’authentification de CAS. Une fois authentifié, l’utilisateur est donc redirigé vers la page login.xhtml qui redirige automatiquement (grâce au on-load) vers la méthode ‘accueil’ du SessionController.

Quand on tente d’accéder à une page de etu/ on-load lance la méthode ‘isLoggue’ du SessionController. Cette méthode retourne ‘null’ (on a accès à la page désirée) si l’utilisateur est reconnu en tant qu’enseignant ou étudiant, ‘error-connexion’ sinon.

sécurité et on-load

En plus de cette protection nous avons placé un « security constraint » (dans le web.xml) qui bloque l’accès direct aux pages.

<security-constraint>

<display-name>Protection of the raw XHTML pages</display-name>

<web-resource-collection>

<web-resource-name>Raw JSF XHTML Pages</web-resource-name>

<description>

All the XHTML pages of the application.

</description>

<url-pattern>/stylesheets/*</url-pattern>

</web-resource-collection>

<auth-constraint>

<description>No roles, so no direct access</description>

</auth-constraint>

</security-constraint>

Publié dans général. 1 Comment »

Organisation des templates dans esup-mondossierweb

Grace à l’utilisation des facelets dans esup-mondossierweb, nous avons pu faire du templating, qui est non seulement utile pour éviter la recopie de code mais aussi pour passer facilement des servlets aux portlets car comme vous le savez les pages d’une portlet ne contiennent pas les balises html et head, contrairement aux pages d’une servlet.

Chaque page de l’application (.xhtml) est donc une composition d’une page template.xhtml, qui affiche ou non l’entête <html>, dont on redéfinie le contenu. Dans le cas de la partie « étudiant » de mondossierweb, chaque page est une composition de template-etu.xhtml qui est une composition de template.xhtml en y ajoutant le template-menu.xhmtl. template-xhtml ne définit que l’entête, qui différe suivant le type de déploiement, et utilise en decoration le template-page.xhtml qui contient les composants redifinissable menu (définit par template-etu.xhtml) et icones (pour ajouter des icones au menu dans la partie enseignant) et inclut le template-icone.xhtml qui affiche le bouton de retour à la page d’accueil dans le cas où l’utilisateur est un enseignant.

Voici par exemple la page index.xhtml pour la partie etudiant :

<?xml version= »1.0″ encoding= »ISO-8859-1″?>
<!DOCTYPE html PUBLIC « -//W3C//DTD XHTML 1.0 Transitional//EN » « http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd »&gt;
<div xmlns:h= »http://java.sun.com/jsf/html &raquo;
xmlns:e= »http://commons.esup-portail.org &raquo;
xmlns:ui= »http://java.sun.com/jsf/facelets »&gt;
<ui:composition template= »/stylesheets/template/template-etu.xhtml »>
<ui:define name= »body »>
<table cellspacing= »0″ cellpadding= »5″ width= »100% » border= »0″>
<tr>
<td nowrap= »true » class= »uportal-channel-table-header »>Accueil</td>
<td width= »100% »></td>
</tr>
</table>
<p align= »justify » class= »uportal-channel-text »>Ce canal a pour but de vous permettre de consulter votre dossier Etudiant. En accédant à votre dossier, vous trouverez les informations suivantes :
<ul>
<li>Etat-civil : Nom, prénoms,
date de naissance, numéro de dossier, baccalauréat…</li>
<li>Adresses : Adresse annuelle, Adresse fixe</li><li>Inscriptions : La liste de toutes vos inscriptions à l’Université…</li><li>Calendrier des exmanens : Dates, Salles, Durées…</li><li>Notes et résultats : Tous vos résultats aux diplômes,
années intermédiaires, éléments pédagogiques et épreuves.</li>
</ul>
</p>
</ui:define>
</ui:composition></div>

Cette page appelle le template-etu.xhtml suivant :

<?xml version= »1.0″ encoding= »ISO-8859-1″?>
<!DOCTYPE html PUBLIC « -//W3C//DTD XHTML 1.0 Transitional//EN » « http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd »&gt;
<div xmlns:h= »http://java.sun.com/jsf/html &raquo;
xmlns:e= »http://commons.esup-portail.org &raquo;
xmlns:ui= »http://java.sun.com/jsf/facelets »&gt;
<ui:composition template= »/stylesheets/template/template.xhtml »>
<ui:define name= »menu »>
<ui:include src= »/stylesheets/template/template-menu.xhtml »/>
</ui:define>
</ui:composition>
</div>

Qui utilise template-menu.xhtml (qui ne sera pas décrit ici) et appelle le template.xhtml (formaté pour le déploiement portlet) suivant :

<?xml version= »1.0″ encoding= »ISO-8859-1″?>
<!DOCTYPE html PUBLIC « -//W3C//DTD XHTML 1.0 Transitional//EN » « http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd »&gt;
<div xmlns:h= »http://java.sun.com/jsf/html &raquo;
xmlns:e= »http://commons.esup-portail.org &raquo;
xmlns:ui= »http://java.sun.com/jsf/facelets »&gt;

<ui:decorate template= »/stylesheets/template/template-page.xhtml »>
</ui:decorate></div>

Qui utilise le template-page.xhtml:


<?xml version= »1.0″ encoding= »ISO-8859-1″?>
<!DOCTYPE html PUBLIC « -//W3C//DTD XHTML 1.0 Transitional//EN » « http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd »&gt;
<div xmlns:h= »http://java.sun.com/jsf/html &raquo;
xmlns:e= »http://commons.esup-portail.org &raquo;
xmlns:ui= »http://java.sun.com/jsf/facelets »&gt;
<ui:composition><table width= »100% »><tr><td>
<ui:insert name= »menu »></ui:insert>
</td><td align= »right »>
<ui:insert name= »icones »></ui:insert>
</td><td align= »right »>
<ui:include src= »/stylesheets/template/template-icone.xhtml »/>
</td></tr></table>
<hr size= »1″ /><p><ui:insert name= »body »>Page Body</ui:insert></p></ui:composition>
</div>

Qui inclut elle-même la page template-icone.xhtml qui affiche un bouton de retour a l’accueil si l’utilisateur est un enseignant. Elle ne sera pas détaillée ici.

Dans le cas de la partie « enseignant » de mondossierweb, chaque page est une composition de template-ens.xhtml qui est une composition de template.xhtml en y ajoutant le composant titre dans le menu. template-xhtml ne définit que l’entête, qui différe suivant le type de déploiement, et utilise en décoration le template-page.xhtml qui contient les composants redifinissable menu (définit par template-ens.xhtml) et icones (pour ajouter des icones au menu) et inclut le template-icone.xhtml qui affiche le bouton de retour à la page d’accueil.

Voici par exemple la page resultatrechercheannuaire.xhtml:

<?xml version= »1.0″ encoding= »ISO-8859-1″?>
<!DOCTYPE html PUBLIC « -//W3C//DTD XHTML 1.0 Transitional//EN » « http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd »&gt;
<div xmlns:h= »http://java.sun.com/jsf/html &raquo;
xmlns:e= »http://commons.esup-portail.org &raquo;
xmlns:ui= »http://java.sun.com/jsf/facelets »&gt;
<ui:composition template= »/stylesheets/template/template-ens.xhtml »>

<ui:define name= »titre »>
Résultat de la recherche
</ui:define>

<ui:define name= »icones »>
<h:form id= »liens »>
<h:commandLink id= »linkmodifrecherche » action= »navigationRechercheAnnuaire »>
<img border=’0′ src=’modify.gif’ alt=’Modifier la recherche’ title=’Modifier la recherche’ />
Modifier la recherche
</h:commandLink>

</h:form>
</ui:define>


<ui:define name= »body »>

<div class=’channel-content’ id=’channel-content-c221-3′>
CONTENU
</div>
</ui:define>
</ui:composition></div>

Qui est une composition de template-ens.xhtml:

<?xml version= »1.0″ encoding= »ISO-8859-1″?>
<!DOCTYPE html PUBLIC « -//W3C//DTD XHTML 1.0 Transitional//EN » « http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd »&gt;
<div xmlns:h= »http://java.sun.com/jsf/html &raquo;
xmlns:e= »http://commons.esup-portail.org &raquo;
xmlns:ui= »http://java.sun.com/jsf/facelets »&gt;
<ui:composition template= »/stylesheets/template/template.xhtml »>
<ui:define name= »menu »>
<div class=’uportal-channel-table-header’>
<ui:insert name= »titre »></ui:insert>
</div>
</ui:define>
</ui:composition>

</div>

Qui est une composition de template.xhtml que nous avons déjà expliqué plus haut.

Ainsi nous avons donc deux pages template.xhtml : une pour le déploiement portlet (template-portlet.xhtml) et une pour le déploiement servlet (template-servlet.xhtml) ; et il suffit de renommer celle désirée en template.xhtml

Voici l’arborescence des fichiers :

Arborescence des templates

Chaque partie de mondossierweb (etudiant et enseignant) se référe à des pages template-etu.xhtml ou template-ens.xhtml qui sont des composition de template.xhtml.

Un petit diagramme récapitulatif:

diagramme template

Intégration de ibatis dans esup-mondossierweb

Nous allons voir comment utiliser ibatis au sein du projet esup-mondossierweb, en l’intégrant à Spring. Nous allons travailler sur un projet ‘esup-mondossierweb’ qui appelle la méthode ‘test’ d’un controller ‘MonController’ lorsque l’on clic sur un lien du type :

<h:commandLink id=”lientest” action=”#{MonController.test}”>
LienTest
</h:commandLink
>

Le but est qu’en cliquant sur ce lien, on puisse lister le contenu d’une table de la base de données. Pensez tout d’abord à importer les librairie utiles pour utiliser ibatis au sein de spring(ibatis-common-2.jar, ibatis-dao-2.jar, ibatis-sqlmap-2.jar). Voici maintenant comment il faut procéder :

Tout d’abord, il faut créer votre objet java, calqué sur la table que vous souhaitez afficher

package test.ibatis;

public class MonObjet {

private String cod_obj;
private String lib_obj;

public MonObjet() {
super();
}

public String getCod_obj() {
return cod_obj;
}

public void setCod_obj(String cod_obj) {
this.cod_ obj = cod_obj;
}

public String getLib_obj() {
return lib_ obj;
}

public void setLib_obj(String lib_ obj) {
this.lib_ obj = lib_ obj;
}

}

Ensuite nous allons créer les fichiers de configuration pour le mapping de ibatis. On créé tout d’abord le fichier ibatis.config.xml dans properties/dao/ibatis

<?xml version=”1.0″ encoding=”UTF-8″?>
<!DOCTYPE sqlMapConfig PUBLIC “-//iBATIS.com//DTD SQL Map Config 2.0//EN”
“http://www.ibatis.com/dtd/sql-map-config-2.dtd”
>
<sqlMapConfig>
<settings useStatementNamespaces=”true”/>
<sqlMap resource=”properties/dao/ibatis/mapping/Objet.xml”/>
</sqlMapConfig>

A noter que la syntaxe change suvant la dtd choisie dans le DOCTYPE. En version 1, on écrit sql-map par exemple.

Puis, on créé le fichier Objet.xml dans properties/dao/ibatis/mapping

<?xml version= »1.0″ encoding= »UTF-8″ ?>
<!DOCTYPE sqlMap PUBLIC « -//iBATIS.com//DTD SQL Map 2.0//EN »
« http://www.ibatis.com/dtd/sql-map-2.dtd &raquo;
>
<sqlMap namespace= »Objet »><resultMap id= »resultobjet » class= »test.ibatis.Objet »>
<result property= »cod_obj » column= »cod_obj »/>
<result property= »lib_obj «  column= »lib_obj « />
</resultMap>

<select id= »getObjets » resultMap= »resultobjet »>
SELECT COD_OBJ as cod_obj, LIB_OBJ as lib_ obj FROM OBJET
</select>

</sqlMap>

On peut ensuite créer l’interface de notre service :

package test.ibatis;

import java.util.List;

public interface IBatisService {

public List getObjets();

}

Et la classe qui l’implémente :

package test.ibatis;

import java.util.List;

import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

public class IBatisServiceImpl extends SqlMapClientDaoSupport implements IBatisService{

public IBatisServiceImpl(){

super();

}

public List getObjets(){

return getSqlMapClientTemplate().queryForList(« Objet.getObjets »,null);

}

}

On peut ensuite configurer notre dao.xml pour qu’il intègre ibatis à String :

<?xml version= »1.0″ encoding= »UTF-8″ ?>

<beans

xmlns= »http://www.springframework.org/schema/beans &raquo;

xmlns:xsi= »http://www.w3.org/2001/XMLSchema-instance &raquo;

xsi:schemaLocation= »http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd &raquo;>

<bean id= »dataSource »

class= »org.springframework.jdbc.datasource.DriverManagerDataSource »>

<property name= »driverClassName »>

<value>nom du driver pour accéder à votre base de données ex: oracle.jdbc.driver.OracleDriver </value>

</property>

<property name= »url »>

<value>url pour accéder à votre base de données</value>

</property>

<property name= »username »>

<value>votre login</value>

</property>

<property name= »password »>

<value>votre password</value>

</property>

</bean>

<bean id= »service » class= »test.ibatis.IBatisServiceImpl »>

<property name= »dataSource »><ref local= »dataSource »/></property>

<property name= »sqlMapClientTemplate » ref= »sqlMapClientTemplate » />

</bean

<bean id= »sqlMapClientTemplate »

class= »org.springframework.orm.ibatis.SqlMapClientTemplate »>

<property name= »sqlMapClient » ref= »sqlMapClient » />

</bean>

 

<bean id= »sqlMapClient »

class= »org.springframework.orm.ibatis.SqlMapClientFactoryBean »>

<property name= »configLocation »

value= »/WEB-INF/classes/properties/dao/ibatis/ibatis.config.xml » />

<property name= »dataSource » ref= »dataSource » />

</bean>

<bean id= »transactionManager »

class= »org.springframework.jdbc.datasource.DataSourceTransactionManager »>

<property name= »dataSource »><ref local= »dataSource »/></property>

</bean>

<bean id= »databaseManagerStore »

class= »org.esupportail.commons.services.database.EmptyDatabaseManagerStoreImpl »>

<description>

This bean is the sore of database managers.

</description>

</bean>

<bean id= »daoService »

class= »org.esupportail.mondossierweb.dao.DaoServiceEmptyImpl »>

<description>

This bean is the DAO service, the interface used by the domain service to access

the database. It must implement interface org.esupportail.mondossierweb.dao.DaoService.

</description>

</bean>

</beans>

 

Il ne reste maintenant qu’a créer le controller qui va appeler la méthod getObjets du service. Il faut penser à injecter le service dans le controller. On ajoute donc dans properties/web/controllers.xml

<bean id= »MonController »

class= »org.esupportail.mondossierweb.web.controllers.MonController »

parent= »abstractContextAwareController »

scope= »session »>

<property name= »service » ref= »service »>

<description>Le service</description>

</property>

</bean>

Il suffit ensuite d’avoir l’attribut suivant dans le controller :

/**

* Le service.

*/

private IBatisServiceImpl service;

avec les getters et setters correspondant :

public IBatisServiceImpl getService() {

return service;

}

public void setService(IBatisServiceImpl service) {

this.service = service;

}

Et on peut enfin créer la méthode test qui sera appelée lors du clic sur le lien ‘lientest’

public String test() {

List<Objet> lobjets = (List) service.getObjets();

System.out.println(lobjets.size()+ » objets »);

for(Objet objet : lobjets){

System.out.println( » – « +objet.getCod_obj()+ » : « +objet.getLib_obj() );

}

//on retourne à la vue

return « navigationPage »;

}

Et voila, on peut maintenant lancer l’application pour tester. Il ne reste qu’à s’inspirer de cet exemple pour simplement effectuer des insert ou des update dans la base ou encore pour afficher le résultat de la requête dans la page html.

Publié dans iBATIS. Leave a Comment »