oct 15

Développement web avec MAVEN - SRING 2.5 – HIBERNATE

Tag: Développement, J2EE, Non classé, Springkarl verger @ 8:48


Table des matières

Mise en place de maven. 3

Mise en place SPRING.. 4

Exemple de fichier web.xml 4

Les fichiers de configurations. 6

applicationContext-service.xml 6

formationspring-servlet.xml 7

Mise en place des controllers. 7

Declaration d’un controller. 7

Explication des annotations. 7

Diagramme de séquence simpliste d’appel à un Controller. 8

Spring security. 8

Authentification et autorisation. 8

Mise en place. 11

pom.xml 11

web.xml 11

applicationContext-security.xml 11

La gestion des transactions. 14

Définition d’une Transaction. 14

Overview d’une transaction applicative. 15

Les niveaux d’isolation des transactions. 15

Les modes de propagation des transactions. 17

Mise en place de HIBERNATE. 18

Le fichier applicationContext-hibernate.xml 18

Validation du domaine métier. 20

Contraintes intégrées. 20

Vérification des erreurs. 22

SQL et HQL Injection. 23

Requête paramétrée HQL. 23

Requite paramétrée SQL. 23

AJAX et DWR. 23

Mise en place. 24

pom.xml 24

web.xml 24

dwr.xml 24

Les problèmes d’ENCODING.. 25

DataBase. 25

Hibernate. 25

Spring framework. 25

Velocity et mails. 25

JSP. 26

Géré les paramètres et les exceptions  utilisateurs web inattendues. 26

La gestion des Tests unitaires. 26

Mesurer la couverture de test. 26

Exemple de test d’un controler web : 27

Mesurer les performances. 29

Mise en place “globale” de JAMon. 29

Paramétrer l’application pour définir les éléments dont on souhaite mesurer les temps d’exécution   29

Suivre le temps d’appel aux pages. 30

Suivre le temps d’exécution de certaines méthodes. 30

Suivre le temps d’exécution des requêtes SQL. 31

Le reporting du projet. 31

JDEPEND.. 32

Analyse des métriques de JDEPEND.. 33

PMD.. 33

 


Mise en place de maven

Installation

Télécharger l’archive maven-2.x.x-bin.zip sur http://maven.apache.org
Décompresser l’archive et copier le répertoire maven-2.x.x à l’endroit de votre choix.

Ajouter une variable d’environnement %MVN_HOME% avec pour valeur le chemin du répertoire d’installation maven-2.x.x et ajouter %MVN_HOME%/bin au PATH.
Dans une console de commande lancer mvn –version pour vérifier que l’installation est correcte ( la version de Maven 2 doit s’afficher ).

 

Tutorial de prise en main : http://dcabasson.developpez.com/articles/java/maven/introduction-maven2/

 

FAQ : http://java.developpez.com/faq/maven/?page=sommaire

 


Mise en place SPRING

Exemple de fichier web.xml

 

<!DOCTYPE web-app PUBLIC “-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN” “http://java.sun.com/dtd/web-app_2_3.dtd” >

<web-app>

 <display-name>formationspring</display-name>  

 <!–    définition des fichiers qui contiendront la déclaration de nos beans    spring    –> 

<context-param>      

 <param-name>contextConfigLocation</param-name>      

 <param-value>/WEB-INF/applicationContext*.xml</param-value>  

 </context-param>     

<!–filtre permettant de forcer l’encoding–>

<filter>

        <filter-name>encodingFilter</filter-name>

        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>

        <init-param>

            <param-name>encoding</param-name>

            <param-value>UTF-8</param-value>

        </init-param>

        <init-param>

            <param-name>forceEncoding</param-name>

            <param-value>true</param-value>

        </init-param>

    </filter>

    <!–filtre Spring permmettant de mettre en place le pattern Session In View Hibernate–>

    <filter>

        <filter-name>Hibernate Session In View Filter</filter-name>

        <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>

    </filter>

    <!–Filtre pour la mise en place de la securité avec ACEGI–>

    <filter>

        <filter-name>springSecurityFilterChain</filter-name>

        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

    </filter>

    <!–application des filtres déclarés plus haut–>

    <filter-mapping>

        <filter-name>encodingFilter</filter-name>

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

    </filter-mapping>

    <filter-mapping>

        <filter-name>Hibernate Session In View Filter</filter-name>

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

    </filter-mapping>

    <filter-mapping>

        <filter-name>springSecurityFilterChain</filter-name>

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

    </filter-mapping>

     <!–

    déclaration du listener spring permettant de charger les beans déclarer

    dans les fichier xml

    –>

    <listener>

        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

    </listener>

    <!–

    déclaration de la servlet spring qui s’occupera traité l’appel au controlleur

    –>

    <servlet>

        <servlet-name>formationspring</servlet-name>

        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <load-on-startup>1</load-on-startup>

    </servlet>

    <!–

    déclaration de la servlet dwr permettant d’appeler des service spring directement

    depuis javascript

    –>

    <servlet>

        <servlet-name>dwr-invoker</servlet-name>

        <display-name>DWR Servlet</display-name>

        <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>

        <!–@todo il est impératif d’enlever le mode debug lors du passage en production–>

        <init-param>

            <param-name>debug</param-name>

            <param-value>true</param-value>

        </init-param>

    </servlet>

<!–déclaration des mapping pour les servlet déclaré ci-dessus–>

    <servlet-mapping>

        <servlet-name>formationspring</servlet-name>

        <url-pattern>*.do</url-pattern>

    </servlet-mapping>

    <servlet-mapping>

        <servlet-name>dwr-invoker</servlet-name>

        <url-pattern>/dwr/*</url-pattern>

    </servlet-mapping>

</web-app>

 

Une fois votre fichier web.xml mis en place, vous pouvez créer vos fichiers xml qui contiendront la déclaration de vos beans spring.

Il est fortement conseiller de découper vos fichiers xml , pour l’exemple je découpe de l a manière suivante :

 

 

 

Les fichiers de configurations

Fichier

Description

applicationContext-aop.xml

Contient la définition des coupes aop et des beans nécessaires aux coupes

applicationContext-hibernate.xml

Contient la description de la sessionFactory hibernate, la descripiton de la datasource et les classes annotée

applicationContext-security.xml

Contient les beans nécéssaire à la mise en place de  la sécurité ainsi que la déclaration des droits

applicationContext-service.xml

Contient les beans métiers

applicationContext-tasks.xml

Contient les taches Schedule

formationspring-servlet.xml

Contient la déclaration des beans pour la partie MVC de spring

dwr.xml

Contient la déclaration des service que l’on veut exposer en javascript via dwr

 

applicationContext-service.xml

Nous déclarons dans ce fichiers les beans en corrélation avec notre couche métier

<?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:context=”http://www.springframework.org/schema/context”

       xmlns:tx=”http://www.springframework.org/schema/tx”

       xmlns:aop=”http://www.springframework.org/schema/aop”

       xmlns:lang=”http://www.springframework.org/schema/lang”

       xsi:schemaLocation=”http://www.springframework.org/schema/beans

       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

       http://www.springframework.org/schema/lang

       http://www.springframework.org/schema/lang/spring-lang-2.5.xsd

       http://www.springframework.org/schema/aop

       http://www.springframework.org/schema/aop/spring-aop-2.5.xsd

       http://www.springframework.org/schema/tx

       http://www.springframework.org/schema/tx/spring-tx-2.5.xsd

       http://www.springframework.org/schema/context

        http://www.springframework.org/schema/context/spring-context-2.5.xsd”>

 

    < !—demande a spring d’outiller et gérer les classes annotéesà

    <context:component-scan base-package=”com.karlverger.formationspring.services” />

    <!—exemple de déclaration d’un bean springà

    <bean id=”mailSender” class=”org.springframework.mail.javamail.JavaMailSenderImpl”>

        <property name=”host” value=”smtp.aix.karlverger.fr”/>

    </bean>

    <bean id=”throwableMessage” class=”org.springframework.mail.SimpleMailMessage”>

        <property name=”subject” value=”ALERT! Exception thrown by ArchiveTimeStampJOB”/>

        <property name=”text” value=”ALERT! Exception thrown from :”/>

        <property name=”from” value=”karl.verger@karlverger.com”/>

        <property name=”to”>

            <list>

                <value>karl.verger@karlverger.fr</value>

            </list>

        </property>

    </bean>

    </beans>

formationspring-servlet.xml

Nous mettons dans ce fichier les déclaration des beans en corrélation avec la partie MVC de spring

<?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:context=”http://www.springframework.org/schema/context”

       xmlns:p=”http://www.springframework.org/schema/p”

       xmlns:aop=”http://www.springframework.org/schema/aop”

       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/aop

       http://www.springframework.org/schema/aop/spring-aop-2.5.xsd”>

   

   <!–on de mande à spring de scanner les ackage que l’on veut appareiller avec les annotation–>

   <context:component-scan base-package=”com.karlverger.formationspring.controlers”/>

  

   <!–on demande a spring de faire matcher les uri des controller avec les jsp–>

   <bean class=”org.springframework.web.servlet.view.InternalResourceViewResolver”

        p:prefix=”/WEB-INF/jsp/”

                        p:suffix=”.jsp”/>

    <!–

        permet de mettre un hook sur les exeception et de rediriger vers des pages

        d’erreurs jsp

    –>

    <bean class=”org.springframework.web.servlet.handler.SimpleMappingExceptionResolver”>

        <property name=”exceptionMappings”>

            <map>

                <entry key=”DataAccessException” value=”data-error”/>

                <entry key=”MissingServletRequestParameterException” value=”params-error”/>

                <entry key=”InvalidStateException” value=”params-validators”/>

            </map>

        </property>

        <property name=”defaultErrorView” value=”general-error”/>

    </bean>

</beans>

 

 

 

Mise en place des controllers

Declaration d’un controller

@Controller

public class IndexControler {

    @RequestMapping(”/formationspring/index.do”)

    public void index(@RequestParam(”companyId”) Long id){

    }

}

 

Explication des annotations

@Controller define votre classe comme un controller et sera managé par spring

@RequestMapping(”/formationspring/index.do”) define l’url d’accès a votre service et le forward vers la jsp si ce n’est pas surcharge

@RequestParam(”companyId”) Long id : permet de définir un paramètre attendu par le controller, ce dernier sera validé et caste par spring, en cas de parmaettre obligatoire (comportement par défault ) spring déclenchera une exception de type MissingServletRequestParameterException

Diagramme de séquence simpliste d’appel à un Controller

Diagramme de séquence simpliste d’appel à un Controller


Il est essentiel de passer par l’annotation @RequestParam afin de fournir une première couche de validation des inputs utilisateurs

Spring security

Authentification et autorisation

La gestion de droit se fait par l’intermédiaire du Framework ACEGI qui est fortement intégré à SPRING et respecte les règles de l’art en la matière.

La prise en charge de la sécurité étant un concept transversal pour une application, nous évitons en utilisant ACEGI de devoir coder la sécurité dans chacun de nos services.

Cela nous permet de proposer une gestion d’utilisateur de sécuriser les requêtes http, de sécuriser la couche de service si nécessaire ainsi que la couche domaine en fonction des  besoins.

Le modèle de données sur lequel on s’appui est le suivant : model de données sprng security entity


Le diagramme ci-dessous décrit la cinématique typique d’une demande d’accès à une ressource sécurisé.

 diag accès ressource

 


Acegi (Spring Security) est le sous-projet de Spring adressant la  sécurité des applications Java/J2EE:

·         Solution très modulaire et indépendante de J2EE.

·         Possibilité de se baser sur les services des serveurs d’application en  matière de sécurité.

·         Solution peu intrusive pour l’application cible.

·         Plusieurs implémentations suivant la stratégie de sécurité choisie  (DAO/JDBC, LDAP, CAS…).

·         Basé sur des intercepteurs (filtres servlet et intercepteurs AOP).

 image004.png

 


Découplage des préoccupations:

·         Application transparente de la sécurité (approche non intrusive  basée sur l’interception des traitements);

·         Récupération des informations pour l’authentification;

·         Gestion de l’authentification;

·         Facilités pour gérer un contexte de sécurité pour le fil d’exécution;

·         Gestion des autorisations;

·         Support pour la couche présentation.

 

 

 

 

Mise en place

La mise en place de la sécurité passe par 3 fichiers

pom.xml

La mise en place d’acegi(spring-security) passe par la dépendance suivante dans le pom.xml:

        <dependency>

            <groupId>org.springframework.security</groupId>

            <artifactId>spring-security-core</artifactId>

            <version>2.0.1</version>

        </dependency>

web.xml

Un filtre dans le web.xml (le fichier complet est fourni en exemple plus haut dans ce document)

    <filter>

        <filter-name>Acegi Security Filter</filter-name>

        <filter-class>

            org.springframework.security.util.FilterToBeanProxy

        </filter-class>

        <init-param>

            <param-name>targetClass</param-name>

            <param-value>

                org.springframework.security.util.FilterChainProxy

            </param-value>

        </init-param>

    </filter>                    

 

    <filter-mapping>

        <filter-name>Acegi Security Filter</filter-name>

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

    </filter-mapping>

applicationContext-security.xml

Ce fichier permet de déclarer les beans et les règles de la politique de sécurité de notre application.

Le fichier est sufisament commenté pour qu’il soit compréhensible.

<?xml version=”1.0″ encoding=”ISO-8859-1″?>

<beans xmlns=”http://www.springframework.org/schema/beans”

       xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”

       xmlns:context=”http://www.springframework.org/schema/context”

       xmlns:jee=”http://www.springframework.org/schema/jee”

       xmlns:util=”http://www.springframework.org/schema/util”

       xmlns:security=”http://www.springframework.org/schema/security”

       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/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd

       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd

       http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd”>   

        <!–

        ==============================================================================

        Context de securité et mise en place des filtres

        ==============================================================================

        –>

        <bean id=”filterChainProxy” class=”org.springframework.security.util.FilterChainProxy”>

            <property name=”filterInvocationDefinitionSource”>

                <value>

                    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON

                    PATTERN_TYPE_APACHE_ANT

                    /j_acegi_security_check*=httpSessionContextIntegrationFilter,authenticationProcessingFilter

                    /**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor,contextHolderAwareRequestFilter

                </value>

            </property>

        </bean>

        <!–

        ==============================================================================

        Bean pour le filtre contextHolderAwareRequestFilter  permettant d’utiliser les methodes J2EE standard pour la securité getRemoteUser() & isUserInRole()

        ==============================================================================

        –>               

        <bean id=”contextHolderAwareRequestFilter”   class=”org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter”/>

        <!–

        ==============================================================================

        Bean permettant de définir les ressources (URL) sécurisées de l’application en ne laissant passé que certains ROLE

        ==============================================================================

        –>               

        <bean id=”filterSecurityInterceptor”   class=”org.springframework.security.intercept.web.FilterSecurityInterceptor”>

            <property name=”authenticationManager”>

                <ref bean=”authenticationManager” />

            </property>

            <property name=”accessDecisionManager”>

                <ref bean=”accessDecisionManager” />

            </property>

            <property name=”objectDefinitionSource”>

                <value>

                    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON

                    PATTERN_TYPE_APACHE_ANT

                    /admin/ *=ADMIN,SUPERUSER

                    /formationspring/*=ROLE_CLIENT

                </value>

            </property>

        </bean>

        <!–

        ==============================================================================

        Bean permettant de gérer la sécurité au niveaux de la couche service metier a l’aide le l’API AOP alliance

        ==============================================================================

        –>               

        <bean id=”secureMyservice” class=”org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor”>

            <property name=”validateConfigAttributes”>

                <value>true</value>

            </property>

            <property name=”authenticationManager”>

                <ref bean=”authenticationManager”/>

            </property>

            <property name=”accessDecisionManager”>

                <ref bean=”accessDecisionManager”/>

            </property>

            <property name=”runAsManager”>

                <ref bean=”runAsManager”/>

            </property>

            <property name=”objectDefinitionSource”>

                <value>

                                                                                                                      <!–

                    com.karlverger.formationspring.service.admin.AdminService.*=ADMIN,SUPERUSER

                                                                                                                      –>

                </value>

            </property>

        </bean>

        <bean id=”runAsManager” class=”org.springframework.security.runas.RunAsManagerImpl”>

          <property name=”key”><value>my_run_as_password</value></property>

        </bean>        

        <!–

        ==============================================================================

        Beans permettant de gérer  toutes les erreurs d’authentifications ou d’authorisation

        si c’est une erreur d’authentification il renverra vers le bean formLoginAuthenticationEntryPoint

        si c’est une erreur d’authorization il renverra vers une page d’erreur access NOT AUTHORIZED (503)

        ==============================================================================

        –>                 

        <bean id=”exceptionTranslationFilter” class=”org.springframework.security.ui.ExceptionTranslationFilter”>

            <property name=”authenticationEntryPoint”>

                <ref bean=”formLoginAuthenticationEntryPoint” />

            </property>

        </bean>       

        <!–

        ==============================================================================

        Beans servant de point d’entré à l’authentification

        ==============================================================================

        –>      

        <bean id=”formLoginAuthenticationEntryPoint” class=”org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint”>

            <property name=”loginFormUrl”>

                <value>/login.jsp</value>

            </property>

            <property name=”forceHttps”>

                <value>false</value>

            </property>

        </bean>       

        <!–

        ==============================================================================

        Beans permettant de mettre en session le contexte de securité ACEGI ce filtre est obligatoire pour le fonctionnement d’ACEGI

        ==============================================================================

        –>                 

        <bean id=”httpSessionContextIntegrationFilter” class=”org.springframework.security.context.HttpSessionContextIntegrationFilter”>

        </bean>

        <!–

        ==============================================================================

        Gestion de l’authentification

        ==============================================================================

        –>               

        <bean id=”authenticationProcessingFilter” class=”org.springframework.security.ui.webapp.AuthenticationProcessingFilter”>

            <property name=”authenticationManager”>

                <ref bean=”authenticationManager”/>

            </property>

            <property name=”authenticationFailureUrl”>

                <value>/login.jsp?login_error=true</value>

            </property>

            <property name=”defaultTargetUrl”>

                <value>/formationspring/index.do</value>

            </property>

            <property name=”filterProcessesUrl”>

                <value>/j_acegi_security_check</value>

            </property>

        </bean>

        <!–

        ==============================================================================

        Beans permettant de déclarer le fournisseur pour les utlisateurs et les roles

        c’est ici que nous faisont correspondre le model ACEGI a notre MPD

        ==============================================================================

        –>                   

        <bean id=”authenticationManager” class=”org.springframework.security.providers.ProviderManager”>

            <property name=”providers”>

                <list>

                    <ref local=”authenticationProvider”/>

                </list>

            </property>

        </bean>

        <!–ce bean permet de mettre en place une comparaisonde password avec leur clef MD5–>

        <bean id=”passwordEncoder” class=”org.springframework.security.providers.encoding.Md5PasswordEncoder”/>

        <bean id=”authenticationProvider” class=”org.springframework.security.providers.dao.DaoAuthenticationProvider”>

            <property name=”userDetailsService” ref=”userService”/>

            <property name=”passwordEncoder” ref=”passwordEncoder”/>

        </bean>

 

        <!–

        ==============================================================================

        Bean  permettant de gérer la stratégie de vote de ACEGI

        ==============================================================================

        –>                   

        <bean id=”roleVoter” class=”org.springframework.security.vote.RoleVoter”>

            <property name=”rolePrefix”>

                <value></value>

            </property>

        </bean>

        <bean id=”accessDecisionManager” class=”org.springframework.security.vote.AffirmativeBased”>

            <property name=”allowIfAllAbstainDecisions”>

                <value>false</value>

            </property>

            <property name=”decisionVoters”>

                <list>

                    <ref local=”roleVoter”/>

                </list>

            </property>

        </bean>

</beans>

La gestion des transactions

Définition d’une Transaction: Une transaction regroupe une série d’opérations dans un tout indivisible. La transaction n’est validé que si chacune des taches unitaires qu’elle regroupent se sont déroulées correctement (COMMIT). Dans le cas contraire (si une des taches unitaires échoue) l’ensemble des données traitées retrouvent leur état initial(ROLBACK).

Une transaction doit respecter les quatre contraintes suivantes dites ACID :

     Atomicité : une transaction doit s’effectuer en tout ou rien ;

     Cohérence : la cohérence des données doit être assurée dans tous les cas, même dans les cas d’erreur où le système doit revenir au précédent état cohérent ;

     Isolation : la transaction va travailler dans un mode isolé où elle seule peut voir les données qu’elle est en train de modifier, cela en attente d’un nouveau point de synchronisation ; le système garantit aux autres transactions, exécutées en parallèle sur le même système, une visibilité sur les données antérieures ;

     Durabilité : lorsque la transaction est achevée, le système est dans un état stable durable, soit à l’issu d’une modification transactionnelle réussie, soit à l’issue d’un échec qui se solde par le retour à l’état stable antérieur.

Overview d’une transaction applicative

les transactions spring permettent d’encapsuler plusieurs appels de méthodes sur différentes couches et d’en assurer le caractère ACID.

overview-Transaction


SPRING permet de les gérer  manière déclarative et programmatique les  transactions.

Typiquement nous pouvons déclarer des rollbacks en fonction d’une exception qui est déclenchée.

Exemple d’une annotation déclarant les règles transactionnelles à suivre pour une méthode :

 

@Transactional(readOnly = false, propagation = Propagation.MANDATORY, rollbackFor = {Exception.class},isolation=Isolation.REPEATABLE_READ)

Les niveaux d’isolation des transactions

Dans le cas d’une concurrence d’accès Il peut arriver que plusieurs transactions travaillent sur les mêmes informations sans que l’une ou l’autre ne soit terminée. Dans ce genre de cas il possible de perdre la cohérence des données.

Nous pouvons rencontrer 3 types de problèmes relatifs aux transactions concurrentes :

     Lecture sale ou ” dirty read ” : Une transaction lit des données contenant un changement non validé d’une autre transaction. Une partie des données peut se révéler fausse en fonction du commit ou du rollback de l’autre transaction.

     Lecture non répétable ou ” nonrepeatable reads ” : Une transaction lit une donnée, une seconde transaction change la même donnée et la première transaction relit la donnée et obtient une valeur différente. La donnée a changé et est incohérente sur la transaction.

     Lecture fantôme ou ” phantom reads ” : Dans une transaction on ré-exécute une requête, retournant un ensemble de données qui satisfont une condition de recherche. Un nouveau jeu de données apparaît entre les deux opération de lecture.

Pour pallier à ce genre de situation nous avons la possibilité de régler la manière dont chacune de nos transactions seront isolées les unes des autres.

TRANSACTION_READ_UNCOMMITTED : La transaction peut lire des données non validées, c’est à dire des données qui ont été modifiées et non validées par des transactions concurrentes. Toutes les erreurs vues ci-dessus peuvent arriver.

Ce niveau est très dangereux dans les environnements stratégiques où des transactions simultanées mettent à jour des données partagées. Il est également inapproprié pour tous les calculs sensibles, comme les opérations de débit/crédit sur comptes bancaires qui doivent adopter un mode d’isolation plus strict.

Ce niveau d’isolation reste approprié si vous savez à l’avance qu’une instance de votre composant ne fonctionne qu’en l’absence de toute autre transaction simultanée. Cependant, dans la plupart des contextes transactionnels, ce niveau d’isolation est insuffisant.

Son principal avantage est la performance, comme le système transactionnel sous-jacent n’a pas besoin de verrouiller les données partagées.

TRANSACTION_READ_COMMITTED : Ce niveau d’isolation fait que l’on ne peut pas lire les changements non validés des transactions concurrentes.

Ce niveau est certainement plus robuste que le mode précédent. Vous ne pouvez plus lire les données écrites, et non validées, ce qui signifie par conséquent que toutes les données que vous lisez sont cohérentes.

Ce mode est fréquemment utilisé pour les programmes qui effectuent des lectures en base pour constituer des rapports sur les valeurs des données.

TRANSACTION_REPEATABLE_READ : Ce niveau d’isolation assure en plus que lire les données plusieurs fois retourneront les mêmes valeurs même si une autre transaction modifie ces données. Les lectures sont répétables.

Ce mode est utile lorsque l’on doit mettre à jour un ou plusieurs éléments de données d’une ressource, comme un ou plusieurs enregistrement d’une BDDR. Il est nécessaire de lire toutes les lignes et de les mettre à jour, en sachant qu’aucune n’est modifiée par d’autres transactions simultanées. Si on choisit de relire l’une de ces lignes ultérieurement au cours de la transaction, on a la certitude qu’elle contient les mêmes données qu’au début de la transaction.

TRANSACTION_SERIALIZABLE : La transaction a les privilèges exclusifs en lecture/ecriture sur des données en les bloquant; les autres transactions ne peuvent ni lire ni écrire sur ces données. C’est le niveau d’isolation le plus contraignant empêchant également les ” phantom reads “.

Ce mode doit être utilisé pour les systèmes stratégiques qui exigent une parfaite isolation transactionnelle. Il assure que les données lues ont été validées, que la lecture reste identique au fil du temps et que de mystérieuses données validées n’apparaissent pas dans la base en cours de traitement en raison de transactions simultanées.

Ce niveau d’isolation doit être utilisé avec parcimonie, car il induit un coût certain. Si toutes les opérations sont réalisées dans ce mode, les performances de la base de données ont tendance à se dégrader très rapidement jusqu’à l’immobilisation éventuelle.

 

 

 

Récapitulatif des différents niveaux d’isolation et de leurs effets respectifs

Isolation Level

Dirty Read

Non Repeatable Read

Phantom Read

TRANSACTION_READ_UNCOMMITTED

possible

possible

possible

TRANSACTION_READ_COMMITTED

aucune

possible

possible

TRANSACTION_REPEATABLE_READ

aucune

aucune

possible

TRANSACTION_SERIALIZABLE

aucune

aucune

aucune

 

Les modes de propagation des transactions

Propagation : REQUIRED

Description :La méthode doit forcément être exécutée dans un contexte transactionnel existant. S’il n’existe pas lors de l’appel, il sera créé.  En cas d’erreur d’une des méthode composant la transaction elle seront toutes annulés

Contexte transactionel appelant

Aucun contexte transactionel appelant

Existe dans la norme EJB3

S’execute dans le contexte existant

Créer un nouveaux contexte

oui

 

Propagation: SUPPORTS

Description :La méthode peut être exécutée dans un contexte transactionnel s’il existe. Dans le cas contraire, la méthode sera exécutée quand même mais hors d’un contexte transactionnel.

Contexte transactionel appelant

Aucun contexte transactionel appelant

Existe dans la norme EJB3

S’execute dans le context existant

Aucune transaction

oui

 

Propagation: MANDATORY

Description :La méthode doit forcément être exécutée dans un contexte transactionnel. Si tel n’est pas le cas, une exception sera levée.

Contexte transactionel appelant

Aucun contexte transactionel appelant

Existe dans la norme EJB3

S’execute dans le context existant

leve une exception

oui

 

Propagation: REQUIRES_NEW

Description :La méthode impose la création d’une nouvelle transaction pour la méthode. Si un contexte transactionnel existe il sera suspendu jusqu’à ce que la nouvelle transaction soit terminé.

Ce comportement est à utiliser si vous avez besoins que la  méthode soit annulée en cas d’erreur sans propager l’annulation à la méthode appelante.

Attention entraîne des pertes de performances en cas d’utilisation abusive.

Contexte transactionel appelant

Aucun contexte transactionel appelant

Existe dans la norme EJB3

Créer un nouveau context

Créer un nouveau context

oui

 

 

Propagation: NOT_SUPPORT

Description :aucun contexte transactionnel n’est appliqué. Peut être util si vous êtes sur que la méthode n’effectue pas d’opération critique.

Contexte transactionel appelant

Aucun contexte transactionel appelant

Existe dans la norme EJB3

aucun contexte transactionel

Hors contexte transactionel

oui

 

Propagation: NEVER

Description :La méthode ne doit pas être exécutée dans un contexte transactionnel. Si tel n’est le cas, une exception sera  levée.

Contexte transactionel appelant

Aucun contexte transactionel appelant

Existe dans la norme EJB3

leve une exeption

Hors du contexte transactionel

oui

Propagation: NESTED

Description :La méthode est exécutée dans une transaction imbriquée si un contexte transactionnel existe lors de son appel. Permet de mettre en place des savepoint si le drivers JDBC sous jacent le permet

Contexte transactionel appelant

Aucun contexte transactionel appelant

Existe dans la norme EJB3

Créer une nouvelle transaction et l’imbrique dans la transaction courante

Crée une nouvelle transaction

non

 

Comportement des transactions en fonction des Exceptions

Les RuntimeException seront rollabcké dans tous les cas, quand aux Exception applicatives ces dernières peuvent être traité et laissé la transaction finir son boulot en fonction des besoins fonctionnels de l’application.

 

Mise en place de HIBERNATE

 

Le fichier applicationContext-hibernate.xml

<?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:context=”http://www.springframework.org/schema/context”

       xmlns:tx=”http://www.springframework.org/schema/tx”

       xmlns:aop=”http://www.springframework.org/schema/aop”

       xmlns:lang=”http://www.springframework.org/schema/lang”

       xsi:schemaLocation=”http://www.springframework.org/schema/beans

       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

       http://www.springframework.org/schema/lang

       http://www.springframework.org/schema/lang/spring-lang-2.5.xsd

       http://www.springframework.org/schema/aop

       http://www.springframework.org/schema/aop/spring-aop-2.5.xsd

       http://www.springframework.org/schema/tx

       http://www.springframework.org/schema/tx/spring-tx-2.5.xsd

       http://www.springframework.org/schema/context

       http://www.springframework.org/schema/context/spring-context-2.5.xsd”>

   

    <context:annotation-config />

    <context:component-scan base-package=”com.karlverger.formationspring.dao” />

   

    <bean id=”dataSource” class=”org.apache.commons.dbcp.BasicDataSource” destroy-method=”close”>

        <property name=”driverClassName” value=”oracle.jdbc.driver.OracleDriver”/>

        <property name=”url” value=”jdbc:oracle:thin:@127.0.0.1:1521:XE”/>

        <property name=”username” value=”adm_ptf”/>

        <property name=”password” value=”adm_ptf”/>

    </bean>

    

    <bean id=”sessionFactory” class=”org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean”>

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

        <!–helper nécéssaire pour la gestion des lob oracleà

        <property name=”lobHandler”>

            <bean class=”org.springframework.jdbc.support.lob.OracleLobHandler”/>

        </property>

       

        <property name=”annotatedClasses”>

            <list>

                <value>com.karlverger.formationspring.entities.User</value>

                <value>com.karlverger.formationspring.entities.Role</value>

                <value>com.karlverger.formationspring.entities.Address</value>

                <value>com.karlverger.formationspring.entities.PersonnesAddress</value>

                <value>com.karlverger.formationspring.entities.Personnes</value>

                <value>com.karlverger.formationspring.entities.AuditSession</value>

            </list>

        </property>

        <property name=”hibernateProperties”>

            <value>

                hibernate.hbm2ddl.auto=update<!–validate | update | create | create-drop–>

                hibernate.cache.use_second_level_cache = true

                hibernate.format_sql = true

                hibernate.use_sql_comments = true

                hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect

                hibernate.cache.provider_class = net.sf.ehcache.hibernate.EhCacheProvider

                hibernate.cache.use_query_cache = true

                                             hibernate.generate_statistics = true

                                               hibernate.cache.use_structured_entries = true

                hibernate.connection.useUnicode=true

                hibernate.connection.characterEncoding=UTF-8

                <!–

                    les properties nécéssaire à la mise en place d’hibernate-search

                    pour le stockage des index

                –>

                hibernate.search.default.directory_provider = org.hibernate.search.store.FSDirectoryProvider

                hibernate.search.default.indexBase = E:/indexes_lucene               

            </value>

        </property>

 

        <!–

            déclaration des listeners hibernate search pour qu’il réalise l’indexation

            en mode synchrones

        –>

        <property name=”eventListeners”>

            <map>

                <entry key=”post-update”>

                    <bean class=”org.hibernate.search.event.FullTextIndexEventListener”/>

                </entry>

                <entry key=”post-insert”>

                    <bean class=”org.hibernate.search.event.FullTextIndexEventListener”/>

                </entry>

                <entry key=”post-delete”>

                    <bean class=”org.hibernate.search.event.FullTextIndexEventListener”/>

                </entry>                       

 

                <entry key=”pre-update”>

                    <bean class=”org.hibernate.validator.event.ValidatePreUpdateEventListener”/>

                </entry>

                <entry key=”pre-insert”>

                    <bean class=”org.hibernate.validator.event.ValidatePreInsertEventListener”/>

                </entry>               

            </map>               

        </property>       

    </bean>

   

    <!– enable the configuration of transactional behavior based on annotations –>

    <tx:annotation-driven transaction-manager=”transactionManager” proxy-target-class=”true” />

    <bean id=”transactionManager” class=”org.springframework.orm.hibernate3.HibernateTransactionManager”>

        <property name=”sessionFactory” ref=”sessionFactory”/>

    </bean>

</beans>

Validation du domaine métier

Hibernate Validator permet de manière élégante d’appliquer une validation sur le domaine métier de notre application.

De plus couplé avec spring cela nous permet d’avoir une chaine de validation simple et efficace.

Contraintes intégrées

Hibernate Validator arrive avec des contraintes intégrées, lesquelles couvrent la plupart des vérifications de données de base. Comme nous le verrons plus tard, vous n’êtes pas limité à celles-ci, vous pouvez écrire vos propres contraintes en une minute.

Tableau 4.1. Contraintes intégrées

Annotation

S’applique à

Vérification à l’exécution

Impact sur les méta-données d’Hibernate

@Length(min=, max=)

propriété (String)

vérifie si la longueur de la chaîne de caractères est comprise dans l’intervalle

la longueur de la colonne sera positionnée à max

@Max(value=)

propriété (nombre ou chaîne de caractères représentant un nombre)

vérifie si la valeur est inférieure ou égale à max

ajoute une contrainte de vérification sur la colonne

@Min(value=)

propriété (nombre ou chaîne de caractères représentant un nombre)

vérifie si la valeur est supérieure ou égale à max

ajoute une contrainte de vérification sur la colonne

@NotNull

propriété

vérifie si la valeur n’est pas nulle

les colonnes sont marquées “not null”

@Past

propriété (Date ou Calendar)

vérifie si la date est dans le passé

ajoute une contrainte de vérification sur la colonne

@Future

propriété (Date ou Calendar)

vérifie si la date est dans le futur

aucun

@Pattern(regex=”regexp”, flag=)

propriété (String)

vérifie si la propriété correspond à l’expression rationnelle donnée (pour “flag”, voir java.util.regex.Pattern)

aucun

@Range(min=, max=)

propriété (nombre ou chaîne de caractères représentant un nombre)

vérifie si la valeur est comprise entre min et max (inclus)

ajoute une contrainte de vérification sur la colonne

@Size(min=, max=)

propriété (tableau, collection, map)

vérifie si la taille de l’élément est comprise entre min et max (inclus)

aucun

@AssertFalse

propriété

vérifie que la méthode est évaluée à faux (utile pour les contraintes exprimées dans le code plutôt que dans les annotations)

aucun

@AssertTrue

propriété

vérifie que la méthode est évaluée à vrai (utile pour les contraintes exprimées dans le code plutôt que dans les annotations)

aucun

@Valid

propriété (objet)

exécute la validation récursivement sur l’objet associé. Si l’objet est une Collection ou un tableau, les éléments sont validés récursivement. Si l’objet est une Map, les éléments valeur sont validés récursivement.

aucun

@Email

propriété (String)

vérifie si la chaîne de caractères est conforme à la spécification d’une adresse e-mail

aucun

 

Vérification des erreurs

Vous pouvez soit valider vos objet de la manière suivante :

            ClassValidator addressValidator = new ClassValidator( Personnes.class );

           

            // vérification des valeurs invalides du domaine

            InvalidValue[] validationMessages = addressValidator.getInvalidValues(personne);

            for (InvalidValue invalidValue : validationMessages) {

                logger.debug(invalidValue.getPropertyName() +” : “+ invalidValue.getMessage());

            }

Soit en levant l’exception org.hibernate.validator.InvalidStateException lors de l’update on l’insert de vos entités. Cette exception est levé par les listeners déclaré dans le fichier applicationContext-hibernate.xml

                <entry key=”post-delete”>

                    <bean class=”org.hibernate.search.event.FullTextIndexEventListener”/>

                </entry>                       

 

                <entry key=”pre-update”>

                    <bean class=”org.hibernate.validator.event.ValidatePreUpdateEventListener”/>

                </entry>

SQL et HQL Injection

Il est absolument nécessaire de passer par querys paramétrées afin d’éviter les problèmes de SQL Injection

Requête paramétrée HQL

String badParameter=”la’ or ‘1′=’1″;
Query reallyBadQuery = session.createQuery(”from Address a where a.street=:street”);
reallyBadQuery.setParameter(”street”, badParameter);

 

Requite paramétrée SQL

String sql = “UPDATE Stocks SET prix = ?, quantite = ? WHERE nom = ?”;
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setBigDecimal(1,15.6);
preparedStatement.setIntl(2,256);
preparedStatement.setStringl(3,“café”);
preparedStatement.executeUpdate();

AJAX et DWR

La gestion des requetes AJAX se fera par l’intermédiaire du framework DWR (Direct Web Remoting) ce dernier permet de consommer des services métier java directement depuis javascript et de pouvoir échanger des objets sérializer à travers le réseau.

 DWR ajax java


Mise en place

La mise en place de dwr passe par 3 fichiers.

pom.xml

vous devez declarer la dependence suivante :

        <dependency>

            <groupId>org.directwebremoting</groupId>

            <artifactId>dwr</artifactId>

            <version>3.0.M1</version>

        </dependency>

 

web.xml

Le fichier complet de l’exemple est fourni au début de ce document

    <servlet>

        <servlet-name>dwr-invoker</servlet-name>

        <display-name>DWR Servlet</display-name>

        <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>

        <!–@todo il est impératif d’enlever le mode debug lors du passage en production–>

        <init-param>

            <param-name>debug</param-name>

            <param-value>true</param-value>

        </init-param>

    </servlet>

 

    <servlet-mapping>

        <servlet-name>dwr-invoker</servlet-name>

        <url-pattern>/dwr/*</url-pattern>

    </servlet-mapping>

dwr.xml

Ce fichier permet de déclarer les beans spring à exposer  en javascript via dwr.

Il est important de bien réfléchir à la déclaration des converters car dwr fait de la réflexion sur les beans, vous risquer donc de vous retrouver avec un graphe d’objet énorme et des performance catastrophique, pour éviter cela penser à bien exclure les propriétés non nécéssaires.

Les méthodes des services doivent être déclarés explicitement afin d’éviter d’exposer des méthodes non nécessaires ou à risque.

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

<dwr>

    <allow>

        <create creator=”spring” javascript=”personneService” >

            <param name=”beanName” value=”personneService”/>

            <include method=”getAllPersonnes”/>

            <include method=”createPersonne”/>

        </create>

       

        <convert converter=”bean” match=”com.experian.formationspring.entities.Personnes”>

            <param name=”exclude” value=”personnesAddressCollection,users”/>

        </convert>

       

        <convert converter=”bean” match=”java.lang.*”/>

        <convert converter=”bean” match=”java.util.*”/>

    </allow>

</dwr>

Les problèmes d’ENCODING

Les erreurs d’encodage sont extrêmement fréquentes, voici donc un petit checkup

  • Database
  • Hibernate
  • Spring MVC
  • Velocity (pour les mails)
  • Tomcat

DataBase

Vérifier que les tables et les champs textes utilisent le charset voulu :

 

Hibernate

                hibernate.connection.useUnicode=true

                hibernate.connection.characterEncoding=UTF-8

– hibernate.cfg.xml –
<hibernate-configuration>
  <session-factory>
    <property name=”hibernate.connection.useUnicode”>true</property>
    <property name=”hibernate.connection.characterEncoding”>utf-8</property>
  </session-factory>
</hibernate-configuration>
 

Spring framework

Utiliser le servlet filter CharacterEncodingFilter dans le web.xml afin de forcer l’encodage. Attention de bien positionner ce filtre en première position dans la liste des différents filtres!

– web.xml –
<web-app>
  <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
      <param-name>forceEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>   
</web-app>

Velocity et mails

Lors du load des templates Velocity, spécifier l’encodage UTF-8.

String content = VelocityEngineUtils.mergeTemplateIntoString(velocityEngine, fileName, “UTF-8″, tokens);
 

Encoder aussi les mails. Dans mon cas avec MimeMessageHelper .

MimeMessageHelper helper = new MimeMessageHelper(message, true, “UTF-8″);
 

JSP

Définir l’encodage des JSP.

<%@ page contentType=”text/html; charset=UTF-8″%>
<%@ page pageEncoding=”UTF-8″%>

Géré les paramètres et les exceptions  utilisateurs web inattendues

 

Spring fournit la possibilité de manière simple de pouvoir de traiter les exceptions déclencher par le framework par exemple dans le cas d’entré non conforme .

 

   <bean class=”org.springframework.web.servlet.handler.SimpleMappingExceptionResolver”>

        <property name=”exceptionMappings”>

            <map>

                <entry key=”DataAccessException” value=”data-error”/>

                <entry key=”MissingServletRequestParameterException” value=”params-error”/>

            </map>

        </property>

        <property name=”defaultErrorView” value=”general-error”/>

    </bean>

 

Dans l’exemple ci-dessus toutes les exception de type DataAccessException redirige l’utilisateur vers la page data-error.jsp etc…

 

La gestion des Tests unitaires

Spring fourni un environnement de test très complet permettant de pouvoir tester l’intégralité des couches sans avoir à déployer quoi que ce soit.

Au minimum chacun des services implémentés doit posséder un test unitaire. Lors de la phase de build le plugin cobertura génèrera un rapport du taux de couverture des tests.

Mesurer la couverture de test

Afin d’obtenir des rapports sur le taux de couverture de test chaque projet doit embarquer le plugin cobertura.

            <plugin>

                <groupId>org.codehaus.mojo</groupId>

                <artifactId>cobertura-maven-plugin</artifactId>

            </plugin>