mercredi 26 janvier 2011

Liste des chapitres qui seront traités prochainement


Mise en oeuvre de Spring en tant que conteneur léger

- L'injection de dépendance.
- Présentation de l'injection par setter et par constructeur, quel type d'injection dans quel cas.
- L'accès programmatique au conteneur.
- La configuration par annotations.
- La mise en oeuvre de la Programmation Orientée Aspect (AOP) avec Spring.
- Le couplage Spring / Junit pour la réalisation de tests unitaires.

 Mise en oeuvre de l'accès aux données

- Couplage Spring / JDBC (JdbcTemplate).
- JPA (et JPA 2) comme API de normalisation de la persistance en java.
- Hibernate comme implémentation de JPA.
- Couplage Spring / JPA.

Le Web

- Le modèle MVC.
- La mise en oeuvre du pattern MVC avec Spring Web-MVC.
- Couplage Spring - JSR 303 (bean validation) pour la validation de la saisie.
- Mise en oeuvre d'Ajax avec Spring MVC.
- Sécurisation des applications avec Spring Security

Intégration avec d'autres technologies

- Spring et les Web Services REST.
- Spring et RMI.
- Spring et JMX.
- Spring et JMS.

jeudi 6 janvier 2011

Accès au contexte d’application Spring


Tout Bean déclaré dans un contexte d’application Spring peut se voir injecter des ressources liées au conteneur s’il implémente certaines interfaces.

BeanNameAware                 : Le nom du Bean tel qu’il est configuré dans le contexte.
BeanFactoryAware              : La BeanFactory du conteneur
ApplicationContextAware    : Le contexte d’application lui-même

 
cela s’avère parfois nécessaire pour une intégration poussée avec le conteneur ou pour profiter de services qu’il fournit. Spring propose un ensemble d’interfaces qui peuvent être implémentées par tout Bean et dont l’implémentation provoque le cas échéant l’injection automatique d’une ressource du conteneur.

Exemple : Accéder au contexte Spring depuis un composant non géré par Spring.

 Pour donner accès au contexte Spring aux composants non gérés par Spring, il suffit d'implémenter un bean géré par Spring, implémentant ApplicationContextAware, qui va exposer des méthodes d'accès au contexte Spring



Il est ensuite nécessaire de configurer cette classe dans le contexte Spring : Le principe est que lors de l'instanciation de la classe ApplicationContextHolder par le conteneur Spring, celui-ci va détecter qu'elle implémente l'interface ApplicationContextAware, et donc lui injecter le contexte

<bean  id="applicationContextHolder" class="com.spring.context.ApplicationContextHolder" />

cas d'utilisation :

        ApplicationContext context = ApplicationContextHolder.getContext();
        IUserService userService   = (IUserService)context.getBean("userService");

  ou directement :

        IUserService userService   = (IUserService)ApplicationContextHolder.getBean("userService");

    






mardi 4 janvier 2011

Spring Dao avec Annotations

l'objectif et d'avoir des requêtes sql sous forme d'annotations.

l'annotation qui représente la requête

package com.springdao.stereotype;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface Query {
    String name();
    String query();
}

l'annotation qui représente la liste des d'annotations Query

package com.springdao.stereotype;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented

public @interface Queries {
    Query[] value();
}

la classe dao.

package com.springdao.dao.impl;



@Queries({
    @Query(name="SELECT_USER_FROM_ID",query="Select * From User Where userId = ?"),
    @Query(name="DELETE_USER",query="DELETE User Where docId = ? and userId = ?"),
    @Query(name="SELECT_All_USER",query="Select * From User"),
})   
public class UserDaoJdbcImpl implements IUserDaoJdbc {

    @Autowired @Qualifier("genericDao")
    private IGenericDao<User> dao;
    @Autowired @Qualifier ("sqlParser")
    private SqlParser sqlParser; 

    public List<User> getRedacteurs(int docId) {
        String sql = sqlParser.getSQL("user","SELECT_USER_FROM_ID");
        return new ArrayList<User>(dao.findAll(sql,
                    new UserMapper(), new Object[]{docId}));
    }
   public int removeRedacteur(String userId, int docId) {
        Object[] fields = {
            docId,
            userId
        };
       String sql = sqlParser.getSQL("user", "DELETE_USER");
        return dao.saveOrUpdate(sql,fields );
    }
   public List<User> getAllUsers() {
         String sql = sqlParser.getSQL("user", "SELECT_All_USER");
         return new ArrayList<User>( dao.findAll(sql, new UserMapper()) );
    }
    ....
}

remarquez ici que sqlParser, récupère le contenu des annotations.

Pour récupérer le contenu des annotation on peut utilisé  post-processeur de Bean qui est appelé pendant le processus de création de tout Bean. L’interface BeanPostProcessor contient deux méthodes, appelées respectivement avant et après les méthodes d’initialisation des Beans. Les méthodes d’initialisation correspondent à des méthodes paramétrées par l’utilisateur et appelées après l’injection des propriétés des Beans.

@Component("sqlParser")
public class SqlAnnotationImpl implements BeanPostProcessor, SqlParser {

    private Map<String, Map<String, String>> sqlAnnotation = new HashMap<String, Map<String, String>>();
   
   
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
       
        Class<?> clazz  = bean.getClass();
        if(clazz.isAnnotationPresent(Queries.class)){
            Queries queries = clazz.getAnnotation(Queries.class);
            Query[] ListQuery    = queries.value();
            for(int i = 0 ; i < ListQuery.length ; i++ ){
                Query query = ListQuery[i];
                Map<String, String> sqlData = new HashMap<String, String>();
                sqlData.put( query.name(), query.query());
                sqlAnnotation.put(beanName, sqlData);
            }
        }
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String arg1)
            throws BeansException {
        return bean;
    }

    @Override
    public String getSQL(String name, String sqlKey) {
        String sql = "";
         if(sqlAnnotation.containsKey(name)){     
            Map<String, String> sqlData = sqlAnnotation.get(name);
            if(sqlData.containsKey(sqlKey)){
                sql = sqlData.get(sqlKey);
            }
        }
        return sql;
    }

}


- fichier  de contexte spring : application-context-dao.xml

<beans ...>
   <!--    
    <context:annotation-config />
    <context:component-scan  base-package="com.springdao.dao" />
   -->   
    <!-- ou  -->
    <bean id="sqlParser" class="com.springdao.dao.sqlparser.impl.SqlAnnotationImpl" />
    <bean id="userDao" class="com.springdao.dao.impl.UserDaoJdbcImpl " />

    ....
</beans>

Generic dao avec Spring Jdbc



Les opérations de base pour la persistance des données sont toutes identiques quelques soit
l’objet à sauvegarder, les méthodes en question sont celles d’un CRUD : enregistrement
(Create), lecture (Read), mise à jour (Update) et suppression (Delete).

La plupart du temps ces méthodes sont répétées et redéfinies dans chacun des DAO de notre
application, la seule différence notable est le type des objets que ces méthodes prennent en
paramètre, cependant avec Java 5 et les generics pourquoi ne pas les écrire une fois pour
toutes dans un DAO générique ? cela est possible et est considéré comme une bonne pratique,
la redondance des données étant à éviter à tout prix.


La classe centrale du support JDBC de Spring se nomme org.springframework.
jdbc.core.JdbcTemplate. Elle propose une API très directe pour effectuer toute opération en base de données, sans se soucier des problématiques telles que la gestion des ressources ou des exceptions. Généralement, un JdbcTemplate est injecté dans DAO, qui l’utilise ensuite pour effectuer toutes ses opérations en base de données


public interface IGenericDao <T extends Serializable>{
           
    T find(String xmlName, String sqlKey,ParameterizedRowMapper<T>   mapper,Object... args);
  
    T find(String sql,ParameterizedRowMapper<T> mapper,Object... args);
   
    T singleResult(String xmlName, String sqlKey,ParameterizedRowMapper<T>  mapper,Object... args);

    T singleResult(String sql,ParameterizedRowMapper<T> mapper,Object... args);

   Collection<T> findAll(String xmlName, String  sqlKey,ParameterizedRowMapper<T> mapper, Object... args);

    Collection<T> findAll(String sql,ParameterizedRowMapper<T> mapper, Object... args);

     int saveOrUpdate(String xmlName, String sqlKey,Object... args);
    
     int saveOrUpdate(String sql,Object... args);
             
}

Dans Spring, il existe des classes de support pour les DAO fondés sur JDBC, il est  préconisions d’utiliser la classe de support : SimpleJdbcDaoSupport. Pour bénéficier de ce support, un DAO doit hériter de cette classe :







public class GenericDaoImpl <T extends Serializable>
                  extends SimpleJdbcDaoSupport implements IGenericDao<T>
{
     
      private SqlParser sqlParser;
               
                public T find(String xmlName, String sqlKey, ParameterizedRowMapper<T> mapper, Object... args) {
                              
                               String sql = sqlParser.getSQL(xmlName, sqlKey);
                               List<T> results = getSimpleJdbcTemplate().query(sql,mapper,args);
                               return  (results.isEmpty() ? null : results.get(0));
                }
               
               
                public T find(String sql, ParameterizedRowMapper<T> mapper, Object... args) {
                               List<T> results = getSimpleJdbcTemplate().query(sql,mapper,args);
                               return  (results.isEmpty() ? null : results.get(0));
                }
               
               
                public T singleResult(String sql, ParameterizedRowMapper<T> mapper, Object... args) {
                               return (T) DataAccessUtils.singleResult(getSimpleJdbcTemplate(). query(sql,mapper,args));
                }
   
             public Collection<T> findAll(String sql, ParameterizedRowMapper<T> mapper, Object... args) {
                               List<T> results = getSimpleJdbcTemplate().query(sql,mapper,args);
                               return results;
                }
            public int saveOrUpdate(String xmlName, String sqlKey, Object... args) {
                               String sql = sqlParser.getSQL(xmlName, sqlKey);
                               return getSimpleJdbcTemplate().update(sql, args);                  
                }
               
      ...
}



L’idée est d’externalisé les déclarations des requêtes SQL dans des fichiers xml. Ce qui ne
permettra de changer les requêtes a l’exécution, car le code SQL n’est pas figé dans le code
java.

Exemple

<sqls name="user">
      <sql name="SELECT_USER_SQL">
            <description></description>
            <query>
               <![CDATA[
            SELECT USR_PRENOM,USR_NOM,USR_MAIL,RLE_ID,USR_ACTIF FROM USR_USER WHERE USR_ID = ?
               ]]>
            </query>
      </sql>
      ...
<sqls>

Signature de la méthode qui permet de retourner le code sql souhaité

String getSQL(String name,String sqlKey);            

Name   : attribut du tag sqls, qui représentera dans la plupart des cas le nom logique du fichier xml 
sqlKey : attribut du tag sql, qui représente le nom de la requête SQL

exemple

String sql = sqlParser.getSQL("user","SELECT_USER_SQL">);



 



2.5) utilisation des Annotations

L’objectif ici et d’avoir des requêtes sql paramétré avec des annotations.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface Query {
      String name();
      String query();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented

public @interface Queries {
      Query[] value();
}


Exemple d’utilisations :

@Queries({
       @Query(name="rechercheParId",query="Select * From Personne Where id = ?"),
       @Query(name="rechercheParNom",query="Select * From Personne Where nom = ?"),
       @Query(name="rechercheParprenom",query="Select * From Personne Where prenom = ?"),
})

Pour parser ces requêtes sql, on utilisera l’introspection sur les classes dao

Map<String, Map<String, String>> sqlAnnotation = new HashMap<String, Map<String, String>>();

Class<?> clazz = bean.getClass();
if(clazz.isAnnotationPresent(Queries.class)){
Queries queries = clazz.getAnnotation(Queries.class);
       Query[] ListQuery   = queries.value();
       for(int i = 0 ; i < ListQuery.length ; i++ ){
             Query query = ListQuery[i];
             Map<String, String> sqlData = new HashMap<String, String>();
             sqlData.put( query.name(), query.query());
             sqlAnnotation.put(beanName, sqlData);
       }
}


L’accès aux sources de données est configuré dans le serveur d'applications web sphere 6. et
pour qu’une application Spring puisse accéder à une ressource telle qu'une source de données
JDBC, elle doit utiliser un fournisseur de ressource géré par ce serveur d'applications. Pour ce
faire, procédez comme suit.

Pendant le développement, consulter la configurez de module WAR avec ça référence de ressource dans le fichier web.xml.

<resource-ref id="ResourceRef_1127121358950">
                               <description></description>
                               <res-ref-name> jdbc/DTB-DS </res-ref-name>
                               <res-type>javax.sql.DataSource</res-type>
                               <res-auth>Container</res-auth>
                               <res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>

Dans le fichier de  configuration de l'application Spring, déclarez un bean qui référence un fournisseur de ressource géré par le serveur d'applications. Affectez à la propriété jndiName la valeur java:comp/env/ suivie de la valeur de la propriété res-ref-name que vous avez déclarée dans la référence de ressource.

Exemple :

<bean id="wasDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
                               <property name="jndiName" value="java:comp/env/jdbc/DTB-DS"/>
                               <property name="lookupOnStartup" value="false"/>
                               <property name="cache" value="true"/>
                               <property name="proxyInterface" value="javax.sql.DataSource"/>
</bean>

Puis n’oublie pas d’injecté la dataSource dans le Bean GenericDaoImpl

<bean id="genericDao" class="com.cacib.gdp.dao.GenericDaoImpl" >
                <property name="dataSource" ref="wasDataSource"  />
              ...
</bean>


Exemple d’interface d’un DAO propre à notre application GDP qui doit pouvoir appeler les méthodes du DAO générique plus d’autres qui lui sont propre :

public interface IUserDaoJdbc {

            List<User> listUsersActif();
            List<User> listUsers();
            int addUser(User user);
            ...
}





Son implémentation, désormais les méthodes find, saveOrUpdate, findAll sont accessibles.

public class UserDaoJdbcImpl implements IUserDaoJdbc {

        private IGenericDao<User> dao;
               
         public User getUserById(String id) {
                           return dao.singleResult("user", "SELECT_USER_SQL", new UserMapper(), new Object[]{id});
         }

         public List<User> listUsers() {
                  return new ArrayList<User>( dao.findAll("user", "SELECT_All_USER_SQL", new UserMapper()) );
          }
     
         public int desactiveUsers() {
                                 return dao.saveOrUpdate("….");
         }

         public boolean isRedacteur(String userId) {
                               String sql  = dao.getSQL("user", "SELECT_IS_REDACTEUR_SQL");
                               int res = dao.getJdbcTemplate().queryForInt(sql,new Object[]{userId});
                               return ( 0 != res ? true : false);
          }
         ...
}

A noté que le Bean dao, sera injecté par Spring.

3.1) Utilisation des Mapper :

Il est préconisé d’utilisé des RowMappers, dont le but est de transformer chaque ResultSet JDBC en un objet.

Exemple:

public class UserMapper implements ParameterizedRowMapper<User>{

      public User mapRow(ResultSet rs, int arg1) throws SQLException {
                               User user = new User();
                               user.setNom(rs.getString("USR_NOM"));
                               user.setPrenom(rs.getString("USR_PRENOM"));
                               user.setEmail(rs.getString("USR_MAIL"));
                               user.setRoleId(rs.getString("RLE_ID"));
                               user.setActif(rs.getInt("USR_ACTIF"));
                               return user;
                }
}


Voici un exemple de fichier de configuration Spring pour l’utilisation du DAO générique, avec la source de données utilisant JNDI


<beans       
     <bean id="wasDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
                               <property name="jndiName" value="java:comp/env/jdbc/DTB-DS"/>
                               <property name="lookupOnStartup" value="false"/>
                               <property name="cache" value="true"/>
                               <property name="proxyInterface" value="javax.sql.DataSource"/>
    </bean>
   
     <bean id="genericDao" class="com.cacib.gdp.dao.GenericDaoImpl" />
     <bean id="userDAO" class="com.cacib.gdp.dao.impl.UserDaoJdbcImpl" />
     <bean id="derogationDao" class="com.cacib.gdp.dao.impl.DerogationDaoJdbcImpl" />
                 
    <bean id="sqlParser" class="com.cacib.gdp.dao.sqlparser.SqlParserXmlImpl">
                               <constructor-arg>
                                               <list>    <!- - fichiers sqls -->
                                                               <value>classpath:sqls/usersqls.xml</value>
                                                               <value>classpath:sqls/derogationsqls.xml</value>
                                               </list>
                               </constructor-arg>
    </bean>
</beans>