Affichage des articles dont le libellé est spring. Afficher tous les articles
Affichage des articles dont le libellé est spring. Afficher tous les articles

jeudi 10 juin 2010

Spring 3.0 et Rest

C'est seulement depuis la version 3.0 de Spring que le MVC supporte les services Rest. Biensur, le framework intègre les principales implémentations tel Jersey ou Restlet, mais dans la partie concernant les web services. La décision d'ajouter une implémentation Rest dans Spring MVC vient de l'orientation prise par le framework:
  • les méthodes des contrôleurs fournissent des vues en résultat
  • des annotations trés similaires à celles de Rest permettent:
    • le binding d'une URL vers un contrôleur
    • le binding des paramétres vers les arguments des méthodes d'un contrôleur

Configuration de base

Configurer l'application web

Pour commencer, il faut déclarer le DispatcherServlet dans le fichier de configuration de l'application web: web.xml.

<web-app>

<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/app-config.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>

</web-app>

En lui spécifiant le paramètre contextConfigLocation, la servlet va charger le context Spring a sa création. Le fichier de context se trouve: /WEB-INF/spring. La servlet doit mapper toutes les URLs du type:

<app_context>/services/*

Configuration Spring

La configuration Spring se fait en deux fichiers différents:

  • le context principal, le fichier app-config.xml, qui contient la configuration du backend: DAO, services et mappers.
  • le context MVC, dans le fichier mvc-config.xml, qui contient la configuration... du MVC.

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">


<mvc:annotation-driven />

</beans>

Cette configuration permet d'utiliser l'annotation @Controller pour déclarer les beans gérés par Spring MVC.

Implémentation d'un contrôleur

L'exemple suivant se base sur la gestion d'objets SampleBean:

public class SampleBean {

private String name;
private long id;
}

Et utilise le SampleController pour effectuer une recherche par id sur les SampleBean.

@Controller
@RequestMapping("/sample")
public class SampleController {

@RequestMapping(value="{id}", method=RequestMethod.GET)
public @ResponseBody SampleBean get(@PathVariable long id) {
return persistence.get(id);
}
}
  1. L'annotation @RequestMapping("/sample") sur la classe permet de mapper toutes les requêtes du type /services/sample sur ce contrôleur. /services étant l'URL sur lequel le DispatcherServlet est mappé.
  2. L'annotation @RequestMapping(value="{id}", method=RequestMethod.GET) sur la méthode get spécifie cette méthode sera appelé pour toutes les URL pouvant mapper avec /services/sample/4 pour les requêtes dont la méthode est GET.
  3. L'annotation @PathVariable permet de spécifier que l'une des valeurs ente {} de l'URL doit être mapper sur cette variable.
  4. @ResponseBody permet de déclarer que la valeur de retour de la méthode sera renvoyer dans le corps de la réponse. Reste à le convertir dans un format quelconque.

Mapper le résultat vers un format spécifique

Pour mettre en place la convertion, il faut configurer un bean de type ContentNegotiatingViewResolver. En Rest, l'opération de mapping des valeurs de retour et des paramètres vers des objets s'appel: négociation. La négociation se fait en ce basant sur la valeur du header Accept de la requête. Pour cet exemple, le mapping se fera vers du JSon. le header Accept aura donc pour valeur: application/json. Spring fournit parmis ces dépendances, le parseur Jackson, qui est utiliser dans ce qui suit:

 <bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver" p:order="1">
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
</map>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</list>
</property>
</bean>

La configuration se fait en spécifiant les formats supportés avec l'attribut mediaTypes et les implémentations des mappers ave la propriété defaultViews. La propriété mediaTypes correspond au contenu du header Accept de la requête reçu par le DispatcherServvlet.

samedi 9 mai 2009

Introduction à Spring ROO

Introduction

Lors du premier keynotes de SpringOne Europe, Ben Alex a fait une démonstration d'un outil permettant de démarrer from scratch un nouveau projet: ROO. C'est un outil de génération de code basé sur les produits Spring, Maven et AspectJ.
Nous allons créer une application web permettant de gérer une liste d'employés.

Premier contact

ROO n'étant qu'en version alpha 2, il n'est pas directement disponible à partir de la page de download de Spring. le bundle est disponible à l'adresse suivante: http://dist.springframework.org/milestone/ROO/spring-roo-1.0.0.A1.zip

ROO se présente sous la forme d'un shell:

La commande hint du shell permet d'obtenir de l'aide par rapport à l'état d'avancement du projet. Par exemple, si aucun projet ROO n'ext contenu dans le répertoire courant, la commande de création de projet est proposé:

La touche TAB peut également fournir une aide. Lorsque c'est possible, elle complète la commande à votre place, ou, elle propose une liste des champs possibles.

Remarque:
La commande exit permet de sortir du shell.

Création du projet

ROO ne crée pas le répertoire racine du projet, il faut donc le faire à la main:
mkdir spring_roo
cd spring_roo


La commande suivante permet de créer le contenu du projet sous la forme d'un projet Maven:
create project -topLevelPackage net.techwatch.spring.roo

Remarque: Pour transformer le projet en projet Eclipse, il suffit d'exécuter la commande Maven:
mvn eclipse:eclipse

Il est également possible d'ajouter les logs par:
configure logging -level DEBUG

Avant la création des entités du model de données, il faut intégrer le support de JPA à notre application:
install jpa -database HYPERSONIC_IN_MEMORY -provider HIBERNATE

et créer les classes du domaine:
new persistent class jpa -name ~.domain.Employee
add field string -fieldName firstName -notNull
add field string -fieldName lastName -notNull
add field date jpa -fieldName birthDate -type java.util.Date

new persistent class jpa -name ~.domain.EmployeeList
add field string -fieldName name -notNull
add field string -fieldName comment -sizeMax 254
add field date jpa -fieldName date -type java.util.Date
add field set jpa -element Employee -fieldName employee


La création d'un élément persistent se fait par la commande:
new persistent class jpa -name

Remarque: Le caractère ~ fait référence au package root de l'application.

Le type du champ fait partie de la commande, par contre, les contraintes sont considérées comme des options. L'utilisation d'un type date nécessite de spécifier l'implémentation à utiliser: java.util.Date ou java.sql.Date.
L'utilisation de l'option -sizeMax est une utilisation de l'implémentation de la JSR 303 (Bean validation). A l'exécution, ROO utilise ses annotations pour vérifier les contraintes.

Remarque: il est possible d'ajouter un champ dans n'importe quelle entité en utilisant l'option -class de la ligne de commande.

Exemple de code généré pour l'entité Employee:
@Entity
@RooEntity
@RooJavaBean
@RooToString
public class EmployeeList {

@NotNull
private String name;

@Size(max = 254)
private String comment;

@Temporal(TemporalType.TIMESTAMP)
private Date createDate;

@OneToMany(cascade = CascadeType.ALL)
private Set employee = new HashSet();
}


L'annotation RooToString ajoute une définition de la méthode toString. L'annotation RooJavaBean se charge d'implémenter les méthodes d'accès et RooEntity permet d'implémenter, entre autre, la validation des contraintes.

Pour compléter l'application, il faut maintenant implémenter le modéle MVC d'une application web. ROO permet d'implémenter cette architecture en se basant sur Spring WebFlow et Spring MVC. Des composants Ajax, basés sur le framework Dojo, sont également intégrés aux pages JSP.

new controller automatic -name ~.web.EmployeeListController
new controller automatic -name ~.web.EmployeeController -formBackingObject ~.domain.Employee


L'utilisation du paramétre automatic permet de générer la totalité de la logique des controlleurs, sinon, son développement est à la charge du développeur.
ROO, lors de la création des controlleurs, ajoute l'option -formBackingObject avec une référence sur la dernière entité utilisé. Cette option permet de faire le lien avec l'entité JPA pour le stockage des données du formulaire.

Exemple de code pour un controlleur:
@RooWebScaffold(automaticallyMaintainView = true, formBackingObject = Employee.class)
@RequestMapping("/employee/**")
@Controller
public class EmployeeController {
}


L'annotation RooWebScaffold permet de faire le mapping entre l'entité et la vue (un peu à la façon dont Grails fonctionne). RequestMapping permet d'établir la concordance entre les URLs et les controlleurs.

Remarque: l'application utilise des URL Rest.

Il ne reste plus qu'à exécuter l'application:

mercredi 29 avril 2009

SpringOne Europe 2009: ou comment sauver Java

Nous sommes sauvés

Premier key notes, rencontre avec Rob Johnson, le père de Spring. Comme entrée en matière, il a choisit de parler de ce qui nous inquiète tous: le rachat de Sun par Oracle. Rob souligne l'attitude de suiveur qu'à toujours eu Sun dans le développement de la plate forme, et que, Java est devenu open source, qu'il n'y a donc pas à s'en faire: les innovations vont continuer comme avant, apporté par d'autres sociétés/communautés. Et puis, il ne faut pas s'en faire, SpringSource est là...
A ce demander s'il na pas raison, vu le nombre de développeurs participant à des JSR de JEE 6.
Ensuite, il continue en parlant de la difficulté qu'ont les développeurs de démarrer un projet rapidement, de mettre en place les environements adéquats et conclut en présentant Spring Tool Suite et ROO, en comparant cette offre à celle de la plate forme .NET. Un peu prétentieux non...

Le contenu

Le contenu de la majeur partie des conférences ressemblait à un résumé de la documentation en ligne. Toutefois, certains projets ont su se mettre en valeur parmis lesquels:
  • tc server: un serveur Tomcat associé à une console digne de la console d'administration WebSphere. Elle intégre des outils de monitoring.
  • Spring Faces: Début de l'intégration de JSF 2.0. Intégre le framework JavaScript DOJO permettant d'intégrer des composants graphique avancés (Spring JavaScript). Ce framework utilise Spring MVC à la place des Manage Bean.
  • Spring RESTFul: une implémentation REST basée sur Spring WebFlow et Spring MVC.
  • Groovy: Un langage que l'on ne présente plus... La conférence sur les DSL en Groovy n'était en fait qu'un copié/collé de la conférence servant d'introduction au langage. Dommage, sachant que la facilité d'implémenter des DSL dans ce langage en est le principal intéret.

En conclusion

Une certaine déception, tout d'abord. Étant plutôt habituer aux rassemblements de geek, c'est une drôle d'impression de se retrouver dans une salle remplie de costumes cravates. L'événement était clairement orienté business et destiné à en mettre plein la vue. Il suffit pour cela de voir l'endroit dans le quel SpringOne a était organisé. Malgrés le standing de l'hotel, je ne peut que déplorer le manque de café ou de rafraichissement entre les conférences.
Le moment le plus intéressant de l'événement fut sans doute lundi soir, le repas qui été organisé permettait de discuter avec les différents intervenants et d'échanger des idées, ce qui, même lors d'événements comme Devoxx est assez rare, et particulièrement appréciable.

mardi 25 novembre 2008

Injection d'une DataSource dans un contexte Spring

Cet article a pour but de décrire comment injecter une datasource dans un contexte Spring existant et surtout de décrire les solutions que j'ai envisagé.

La problématique

Dans le cadre du développement d'une API devant interroger une base puis traiter ces données, les contraintes techniques suivantes sont apparues :
  • une seule classe doit servir de façade pour utiliser l'API,
  • celle-ci doit prendre une DataSource définit par le client,
  • la partie cliente ne doit pas avoir connaissance du contexte Spring de l'API.
Sachant que la partie cliente est dépendante du framework Spring, son utilisation dans ce cas de figure semblait naturel. Ainsi, la partie ORM du framework permet de coder plus rapidement les accès à la base et le fichier de contexte permet de résoudre les problèmes d'injection de dépendance, de transaction et permet d'éviter l'écriture de fabriques.
Les beans d'accès aux données ainsi que les beans gérant les transactions ont besoin d'une DataSource pour pouvoir fonctionner.
Comment faire pour créer le bean d'entrée de la librairie, donc charger le contexte, sans qu'une exception soit lancé par Spring? Comment faire pour que Spring s'occupe de l'injection de la DataSource alors que celle-ci n'est connu qu'au moment d'utiliser la librairie?

Quelques solutions

Injecter manuellement la DataSource

L'idée ici est de créer le bean façade de l'API, d'y injecter les dépendances et d'injecter manuellement la DataSource.
Cette solution oblige le développeur à connaitre le nom des beans dépendants de la DataSource. Toute modification du contexte implique alors d'intervenir dans le code.

Injection de la DataSource par Spring

Cette fois ci, il s'agit de laisser Spring injecter la DataSource dans les beans.

La méthode d'initialisation du bean:

public void init(DriverManagerDataSource driverDataSource) throws EngineException {
// premièrement, création d'un contexte parent
ConfigurableApplicationContext genericContext = new GenericApplicationContext();
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) genericContext.getBeanFactory();

// création du bean datasource et injection de ses propriétés
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(DriverManagerDataSource.class); beanDefinitionBuilder.addPropertyValue("url", driverDataSource.getUrl());
beanDefinitionBuilder.addPropertyValue("password", driverDataSource.getPassword());
beanDefinitionBuilder.addPropertyValue("username", driverDataSource.getUsername());
beanDefinitionBuilder.addPropertyValue("driverClassName", "oracle.jdbc.OracleDriver");

// enregistrement du bean dans le contexte parent
beanFactory.registerBeanDefinition("parentDataSource", beanDefinitionBuilder.getBeanDefinition());
genericContext.refresh();

// création du contexte de l'API comme contexte enfant
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "engine-context.xml", "service-context.xml", "dao-context.xml" }, true, genericContext);

// déclare le bean courant comme autowired
context.getAutowireCapableBeanFactory().autowireBeanProperties(this,AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);

// initialise le bean courant en y injectant les beans
context.getAutowireCapableBeanFactory().initializeBean(this,"modeDataEngine");
}

Contexte Spring

La ligne suivante définit une dataSource dans le contexte de l'API.

<bean id="datasource" parent="parentDataSource">


Quelques remarques

La datasource utilisée est une DriverManagerDataSource fournie par le framework Spring. Pourquoi est-ce la seule utilisable?
Pour définir une datasource dans un contexte, nous avons besoin de lui fournir plusieurs données pour s'initialiser:
  • l'URL de connexion,
  • la classe du driver JDBC,
  • le nom d'utilisateur,
  • Le mot de passe.
Le DrvierManagerDataSource est une classe qui permet d'obtenir ces informations en utilisant de simples setter, ce qu'un grand nombre d'implémentation de l'interface DataSource ne font pas.
L'objectif est de proposer une API, ce qui implique qu'elle ne doit prendre en entrée, pour rester flexible, que des interfaces ou des classes définies par l'API elle-même.

Utilisation d'une factory

Une autre solution consiste à créer une factory de DataSource.
L'idée est de fournir une dataSource, n'importe quelle implémentation, à la méthode d'initialisation de l'API, qui ensuite, chargera le contexte Spring. Dans ce contexte, une factory doit être définit. Elle doit définir une méthode d'initialisation, qui ira chercher la dataSource dans l'instance de l'API, puis la fournira aux autres beans.

Contexte Spring

<bean id="dataSourceFactory" class="DataSourceFactory" method="init">

<bean id="dataSource" bean="dataSourceFactory" method="getDataSource">