Overblog Suivre ce blog
Administration Créer mon blog
16 mars 2012 5 16 /03 /mars /2012 22:04

 

spring-logo.jpeg

 

The purpose of this tutorial is to explain how to do a minimal REST Application with Spring MVC in a Maven project. 

This is illustrated by the creation of a REST Controller able to Create, Update, Delete some entity Computer.

 

So in this tutorial we will use this java class

 

public class Computer {

 

  private long id;

  private String brand;

  private String model;

  private String reference;

  private String description;

 

    // getter and setter ...

}

 

and some JSON computer objects as in this example :

{

     "id":1,"reference":"MACR","description":"Test","model":"Macbook Air","brand":"Apple"

}

 

I will not talk here about how to do a REST service really REST compliant, about best practice, or explain what exactly REST is.

I will just show how to implement 4 Basic REST Http Methods with Spring MVC, and how configure Spring MVC to do this. 

If you are in a rush you can go here : http://www.davidgimelle.com/src/SpringREST.zip, dowload this project, install it with Maven, deploy it on an application server and go there http://localhost:8080/SpringREST/rest/computer to see if it works. It should return the content of an empty json array []. But all the same it might be worth reading this first.

 

0) Introduction about REST and Spring MVC annotation in 10 lines

Spring MVC for REST uses the JSON format to exchange data with HTTP protocole. Spring MVC manages Http requests /responses and makes transformations JSON<->Java pojo.  

REST uses mainly 4 Http methods GET, POST, PUT and DELETE. If you are new to REST you should read this paragraph in wikipedia : http://en.wikipedia.org/wiki/Representational_state_transfer#RESTful_web_services

 

Spring MVC provides some annotation to manage Http. In this tutorial i will use only 5 :

@Controller --> Declare a Spring MVC Controller

@RequestMapping("/computer") --> Map an URI with a controller or a method

@PathVariable --> Read a variable in an Uri and assign this value to a java variable

@RequestBody --> Declare a Pojo class to map the http request body  

@ResponseBody --> Declare a Pojo class to generate Json content to return to the http client

 

1) Create a controller with a GET method      

So let see the declaration of a Controller in Java.

The Class ComputerController is a controller to handle all REST requests about entities Computer.

 

// Declaration as this class as a controller

@Controller 

// Declaration that this controller handles requests on uri */computer

@RequestMapping("/computer"

public class ComputerController {

 

 // Stores computers

 private static final ComputerStorage computerStorage = new ComputerStorage();

 

 // Declare a method that handle all GET request on */computer

 @RequestMapping(method = RequestMethod.GET)

 // Return a list of computer to the http client

 public @ResponseBody List<Computer> getComputers() {

  return computerStorage.getAll();

 }

}

 

2) Configure the application

It's a standard Spring mvc configuration, with nothing special for REST.

- Spring mvc must be declared in the web.xml :

 

 <servlet>

   <servlet-name>mvc-dispatcher</servlet-name>

   <servlet-class>

    org.springframework.web.servlet.DispatcherServlet

    </servlet-class>

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

 </servlet>

 

 <servlet-mapping>

   <servlet-name>mvc-dispatcher</servlet-name>

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

 </servlet-mapping>

 

 <context-param>

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

   <param-value>

    /WEB-INF/mvc-dispatcher-servlet.xml

   </param-value>

 </context-param>

 

 <listener>

   <listener-class>

    org.springframework.web.context.ContextLoaderListener

    </listener-class>

 </listener>

 

The Servlet mapping of the mvc-dispatcher redirects all requests on */rest/* to Spring MVC.

So it's possible to request the computer controller by a GET on http://localhost:8080/SpringREST/rest/computer .

 

SpringREST is the default name of the application, you can change it when you deploy the war. 

 

- The file mvc-dispatcher-servlet.xml discribes REST controllers. Because we use annotation base declaration of controllers, we just need to set witch packages contain controllers with the context:component-scan declaration.

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

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

xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="

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

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

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

http://www.springframework.org/schema/context/spring-context-3.0.xsd

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

http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

 

 <context:component-scan 

 base-package="com.davidgimelle.springrest.tuto" 

 />

 

 <mvc:annotation-driven />

 

</beans>

 

 <display-name>Spring REST Application</display-name>


3) Add methods POST, PUT and DELETE to the controller

Now complete the controller with 3 more methods.

 

- The POST method add one computer. 

@RequestMapping(method = RequestMethod.POST)

@ResponseBody 

public void addComputer(@RequestBody Computer computer) {

  computerStorage.add(computer);

}

@RequestBody indicate to the server that the json content of the request is use to fill a new Computer instance.

@ResponseBody alone is required on void method to return no content to the REST Client .

 

- The PUT method update one computer. 

@RequestMapping(value="{id}",method = RequestMethod.PUT)

@ResponseBody

public void putComputer(@PathVariable long id, @RequestBody Computer computer) {

  computer.setId(id);

  computerStorage.update(computer);

}

Following the rest convention, the id of the entity to update is set in the uri like in this example : PUT on http://localhost:8080/SpringREST/rest/computer/6 .

Set the id in the uri and not in the json content respect the REST convention. You can see the PUT convention here : http://en.wikipedia.org/wiki/Representational_state_transfer#RESTful_web_services .

 

@RequestMapping completes the request mapping of the controller with the id and the @PathVariable assigns the value to the long id.

 

-A DELETE method delete a computer.

@RequestMapping(value="{id}", method = RequestMethod.DELETE)

@ResponseBody

public void deleteComputer(@PathVariable long id) {

  computerStorage.delete(id);

}

 

4) How to test it ?

You can  use RestClient, a firefox's plugin to test the Rest application : https://addons.mozilla.org/en-US/firefox/addon/restclient/ . 

 

  - Start by testing a GET on http://localhost:8080/SpringREST/rest/computer .

The response body (Visible in the panel Response Body of Rest Client Plugin) will be an empty array because there is no data yet : []

 

  - Test POST http://localhost:8080/SpringREST/rest/computer

Before sending the request, add an http header Content-Type with the button Add Request Header of Rest client : Content-Type:application/json; charset=utf-8

add a JSON content in the request body 

  {"reference":"MAC","description":"Test","model":"MB Pro","brand":"Apple"}

  - Now the GET should return 

  [{"id":1,"reference":"MAC","description":"Test","model":"MB Pro","brand":"Apple"}]

 

  - Do a second POST

  - Now the GET should return 

 [{"id":1,"reference":"MAC","description":"Test","model":"MB Pro","brand":"Apple"},  {"id":2,"reference":"MAC","description":"Test","model":"MB Pro","brand":"Apple"}]

 

  - Delete a computer with a PUT on http://localhost:8080/SpringREST/rest/computer/2

  - GET return now

 [{"id":1,"reference":"MAC","description":"Test","model":"MB Pro","brand":"Apple"}]

 

  - Update a computer with a PUT on http://localhost:8080/SpringREST/rest/computer/1 with

Content-Type:application/json; charset=utf-8

add a JSON content in the request body 

 {"reference":"MACR","description":"Test","model":"Macbook Air","brand":"Apple"}

  - GET return 

 [{"id":1,"reference":"MACR","description":"Test","model":"Macbook Air","brand":"Apple"}]

 

 

5) Jackson JSON Processor

Jakson JOSN Processor is used by Spring to make the mapping JSON <-> Java Object. Since spring 3.0.2, it uses by default if it presents in the classpath of the application.

 

6) How to use Maven ?

It's the same declaration that for Spring MVC with a dependency on JSON Processor : 

<dependency>

     <groupId>org.codehaus.jackson</groupId>

    <artifactId>jackson-mapper-asl</artifactId>

    <version>1.7.1</version>

</dependency>

You can find the entire pom.xml it in the source of this article.

 

7) Where are the source of this article ?

You can dowload it here : http://www.davidgimelle.com/src/SpringREST.zip

 

Thanks for reading. The next time i will talk about more advanced features such as Handle Error or Http parameters.

 

 

Links

REST : http://en.wikipedia.org/wiki/Representational_state_transfer

 

An other tutorial : http://www.mkyong.com/spring-mvc/spring-3-mvc-and-json-example

Springsource example : http://static.springsource.org/spring-security/site/petclinic-tutorial.html

Jackson JSON processor : http://jackson.codehaus.org

REST and HTTP : http://www.packetizer.com/ws/rest.html

Firefox Rest Client :  https://addons.mozilla.org/en-US/firefox/addon/restclient

 

Technical Specifications

Spring mvc 3.0.5 use Jackson JSON processor 1.7.1 to marchal/unmarchal Java/JSON

Tested 11th March 2012 with Spring 3.0.5, Glassfish 3.1.1, Java 1.6.0_29, Mac OSX 10.6.8

 

Repost 0
Published by David Gimelle - dans Tutorial
commenter cet article
26 janvier 2011 3 26 /01 /janvier /2011 17:41

 

jquery.jpeg

Voici comment faire une requête Ajax de type GET avec JQuery coté navigateur, RESTEasy coté Serveur et JSON entre les 2.

 

Les sources complètes de ce projet sont disponible sous la forme d'un projet maven téléchargeable à la fin de ce billet.

 

 

Ajax, JQuery, JSON, RESTEasy qu'est ce que c'est ?

 

Ajax : Ajax est une technologie permettant de faire des requêtes asynchrones depuis un navigateur web vers un serveur web avec http en utilisant Javascript.  Ajax permet via du javascript d'aller faire un appel à n'importe quel serveur web sans changer de page web dans le navigateur. 

 

JQuery :  JQuery est une bibliothèque Javascript Crossbrowser, c'est a dire qu'un code javascript écrit avec Jquery fonctionnera aussi bien avec Firefox que IE ou Opera. JQuery permet de faire des requetes Ajax.

 

GET : Les méthodes d'appel Ajax dans une architecture REST sont les méthodes HTTP classiques principalement GET et POST. Dans ce billet nous allons faire un GET HTTP.

 

JSON : Un format de données utilisable directement en Javascript. Il ressemble à du XML ultra simplifié. C'est ce format qui sera utilisé pour les échanges de données entre le serveur web et jquery

 

RESTEasy : Une implementation de REST , repectant la spécification JAX-RS, proposé par JBOSS pour publier des ressources REST.

 

Voici comment vous s'enchaîner ces technologies lors de l'exécution du Hello world de ce tuto :

 

- L'utilisateur ouvre une page html qui contient du code JQuery

- Il clique sur le bouton d'essai, ce qui déclenche une fonction javascript

- Cette fonction javascript utilise JQuery pour faire un GET sur le serveur RESTEasy

- Le serveur RESTEasy génère un fichier JSON et l'envoie à JQuery

- JQuery récupère le fichier JSON et le fourni à javascript

- Une boite d'alerte javascript affiche le contenue du fichier JSON

 

 Assez de théories, passons à la pratique, grace à ce tutorial en 5 étapes.

 

 

Mise en place de la ressource REST avec RESTEasy

 

Mise en place du serveur REST . Cette partie à déjà été décrite dans mon précédant billet sur RESTEasy : Mini Tutorial : REST Web Service avec JBoss RESTEasy . Aussi je ne vais reprendre ici que les grandes lignes.

 

1 - Déclaration des dépendances et des repositories dans le pom maven :

    Le repository de jboss est nécessaire pour récupérer la version 2.0-beta-2 de RESTEasy.

 

<repositories>
        <repository>
            <id>maven repo</id>
            <name>maven repo</name>
            <url>http://repo1.maven.org/maven2/</url>
        </repository>
        <!-- For resteasy -->
        <repository>
            <id>jboss</id>
            <name>jboss repo</name>
            <url>http://repository.jboss.org/maven2</url>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jaxrs</artifactId>
            <version>2.0-beta-2</version>
            <!-- filter out unwanted jars -->
            <exclusions>
                <exclusion>
                    <groupId>commons-httpclient</groupId>
                    <artifactId>commons-httpclient</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>javax.servlet</groupId>
                    <artifactId>servlet-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jettison-provider</artifactId>
            <version>2.0-beta-2</version>
        </dependency>
    </dependencies>

 

2 - Déclaration de RESTEasy dans web.xml

 Voir ce billet : Mini Tutorial : REST Web Service avec JBoss RESTEasy

 

3 - Déclaration de la Classe World pour générer du JSON

Créons une classe World avec un champs message. Notre objectif est de faire passer un objet World Java avec son message dans le javascript du navigateur sous forme d'un objet World JSON avec le même message.

 

package com.davidgimelle.tutorial.resteasyjquerygetjson.element;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "world")
public class World {

    private String message;

    @XmlElement
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

 

Les annotations XML vont être transformées lors de l'utilisation de RESTEasy en JSON. Et ainsi l'instance Java suivante 

  World myWorld  = new World();

  myWorld.setMessage("Hi");

va devenir le fichier JSON suivant dans le navigateur

  {"world":{"message":"Hi"}}

 

4 - Déclaration du service RESTEasy pour publier la classe World

Cette classe va lire un message reçu depuis le navigateur, générer une instance de World et la transformer en JSON.

A ce stade du projet, une fois déployée cette ressource est disponible sous l'url :  http://localhost:8080/RESTEasyTutorialJQueryGetJSON/rest/hello/world/Hi

 

L'annotation @Produces(MediaType.APPLICATION_JSON) précise que le format envoyé par la ressources est JSON. Mais il aurait très bien pu être du XML, ATOM ou autres.

Pour comprendre la significations des autres annotations consulter ce billet : Mini Tutorial : REST Web Service avec JBoss RESTEasy

 

package com.davidgimelle.tutorial.resteasyjquerygetjson;

import com.davidgimelle.tutorial.resteasyjquerygetjson.element.World;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("hello")
public class HelloWorld {

    @GET
    @Path("/world/{message}")
    @Produces(MediaType.APPLICATION_JSON)
    public World helloWorldJSON(@PathParam("message") String message){
        World res=new World();
        res.setMessage(message);
        return res;
    }

}

 

La partie serveur RESTEasy est terminée, passons à la partie client.

 

 

Ecriture d'une fonction d'appel Ajax avec JQuery

 

5 Déclaration d'une page HTML executant JQuery

 

  <html>
    <head>
        <title>RESTEasy Tutorial JQuery Get JSON</title>

         <script type="text/javascript" src="inc/jquery-1.4.2.min.js" language="JavaScript"></script>
         <script type="text/javascript">
            function getHelloWorld(message){
                $.getJSON("./rest/hello/world/"+message,
                    function(dataJson){
                        alert("Message extract from Json:"+dataJson.world.message);
                    }
                );
            }
         </script>

    </head>
    <body>
        For try an Get JQUERY with 'Salut' value, try this button : <button onclick="getHelloWorld('Salut');" >Salut</button>
    </body>
</html>

 

- La ligne <script type="text/javascript" src="inc/jquery-1.4.2.min.js" language="JavaScript"></script> permet d'inclure dans la page la bibliothèque JQuery qui se trouve sur le même serveur que la page html dans le repertoire inc

 

- Le script javascript est la  function getHelloWorld(message)

 

- La fonction JSON est

$.getJSON("./rest/hello/world/"+message,
                    function(dataJson){
                        alert("Message extract from Json:"+dataJson.world.message);
                    }
                );

 

- Le $ est un alias pour appeler la bibliothèque JQuery

- getJSON est la fonction jquery qui va faire un appel GET Ajax sur une ressources JSON

./rest/hello/world/"+message est l'url du serveur, plus la valeur du message à envoyer au serveur

 

- function(dataJson) est la fonction de callback qui est executée dès que JQuery a recu la reponse du serveur. 

dataJson contient les infos json renvoyées par le serveur

dataJson.world.message permet de récupérer la valeur du message dans l'objet JSON

 

C'est terminé. :)

 

 

Sources : 

Les sources complétes de ce tutorial sont disponibles sous forme d'un projet maven  téléchargable : RESTEasyTutorialJQueryGetJSON-src

 

Testé le 18 juillet 2010 avec :

resteasy-jaxrs 2.0-beta-2 , JBoss 4.2.3, Java jdk 1.5_20, maven 2.0.9 et netbeans 6.7.1

 

Liens et références :  

Mini tutorial sur RESTEasy : Mini Tutorial : REST Web Service avec JBoss RESTEasy

Ajax : http://en.wikipedia.org/wiki/Ajax_%28programming%29

Crossbrowser : http://en.wikipedia.org/wiki/Cross-browser

JQuery : http://jquery.com/

JSON : http://www.json.org/

Les exemples de RESTEasy : http://sourceforge.net/projects/resteasy/files/Resteasy%20JAX-RS/

JQuery et JSON: http://api.jquery.com/jQuery.getJSON/

RESTFull : http://tomayko.com/writings/rest-to-my-wife

REST: http://en.wikipedia.org/wiki/Representational_State_Transfer

 

Repost 0
Published by David Gimelle - dans Tutorial
commenter cet article
30 mars 2008 7 30 /03 /mars /2008 22:17

L'objet de ce tutoriel est de compiler tous les billets que j'ai publiés sur les nouveautés de SCJP 6.

Ce tutoriel peut etre utilisé en complement d'un livre sur SCJP 5 pour preparer l'examen sur SCJP 6.

Sommaire :
1 - Liste des differences entre SCJP 5 et SCJP 6
2 - La classe Console
3 - La classe NavigationSet
4 - La classe NavigationMap
5 - Le comportement de Object.finalize()
6 - Liens


1 - Liste des differences entre SCJP 5 et SCJP 6
Fin 2007 est sortie la nouvelle certification Sun Certified Programmer for the Java Platform, Standard Edition 6 (CX-310-065) ou SCJP 6.

Il y a seulement 4 differences entres les 2 certifications:

- Utilisation de la classe Console
- NavigableMap
- NavigableSet
- Abandon de System.gc() et ajout de Object.finalize()


2 - La classe Console
La classe Console permet la saisie de caracteres par l'utilisateur de la console.

Voici un petit programme de test de la classe Console. Ce programme est à lancer depuis une console ! Et non depuis eclipse ou netbeans. ;-) Car les consoles d'eclipse et de netbeans ne sont pas interactive.

package com.getj2ee.tutoscjp6;
import java.io.Console;

/**
* Test java.io.Console
* @author davidgimelle - 02/02/2008

*/
public class TestConsole {

public static void main(String[] args) {
System.out.println("Test TutoSCJP6");
Console console = System.console();

// Test if console exist
if (console == null)
System.out
.println("Error - console is null! :-(. "+

"For use the Console class you need launch"+
" the java programme from a console!"+
" Not from a EDI like eclipse or netbeans. "+
"See : http://java.sun.com/javase/6/docs/api/java/io/Console.html");

// Print a message in the console
console.printf("OK - The Console is ready %n", "");

// Read a line in the console
String oneLine = console.readLine();
System.out.println("OK - read this line : " + oneLine);
}


}


Ce programme a été testé avec Windows Vista, Java 1.6.0_04 le 02/02/2008

Astuce
- Pour ceux qui ont oublié comment on lance un programme java depuis une console :
c:/projeteclipse/tutoscjp6/bin> java com.getj2ee.tutoscjp6.TestConsole
- Pour verifier que le jre courant est bien java 6
c:>java -version

 

3 - La classe NavigationSet
NavigableSet est un Set dans lequel il est possible de naviguer et d'extraire simplement des sous-ensembles.

Par exemple:
 - Les methodes first et last permettent d'obtenir le premier et le dernier element du Set. Le classement se fait grace à la methode CompareTo des elements ou aux methodes de Comparable.

- La methode floor(String s) retourne le premier element après la valeur de s.
 
Voici un petit programme d'exemple d'utilisation de NavigableSet.

package com.getj2ee.tutoscjp6;

import java.util.NavigableSet;
import java.util.TreeSet;

public class TestNavigableSet {

 public static void main(String[] args) {
  System.out.println("Test TutoSCJP6 NavigableSet");
  
  NavigableSet<String> navigableSet = new TreeSet<String>();
  
  navigableSet.add("Pierre");
  navigableSet.add("Paul");
  navigableSet.add("Jacque");
  navigableSet.add("Yves");
  navigableSet.add("Xavier");
  
  String first = navigableSet.first();
  String last = navigableSet.last();
  String floor = navigableSet.floor("Pa");
  NavigableSet<String> headSet = navigableSet.headSet("Paul",false);
  
  System.out.println("first:"+first);
  System.out.println("last:"+last);
  System.out.println("floor:"+floor);
  for (String string : headSet) {
   System.out.println("Head Set:"+string);
  }
 }

}

Ce programme a été testé avec Windows Vista, Java 1.6.0_04 le 02/02/2008



4 - La classe NavigationMap
Un peu comme NavigableSet, NavigableMap est une map dans laquel il est possible de naviguer.
 
Voici un petit programme d'exemple d'utilisation de NavigableMap.

public class TestNavigableMap {
public
static void main(String[] args) {

System. out.println("Test TutoSCJP6 NavigableMap");

NavigableMap<String, String> navigableMap =
new
TreeMap<String, String>();
navigableMap.put(
"Moose", "Elan"
);
navigableMap.put(
"Reinder", "Renne"
);
navigableMap.put(
"Beaver", "Castor"
);
navigableMap.put(
"Bear", "Ours"
);
navigableMap.put(
"Whale", "Baleine"
);

String first = navigableMap.firstKey();
String last = navigableMap.lastKey();
String floor = navigableMap.floorKey(
"R"
);

SortedMap<String, String> headMap = navigableMap.headMap(
"Bear"
);
System.
out
.println("first:"
+ first);
System.
out
.println("last:"
+ last);
System.
out
.println("floor:" + floor);

for (String key : headMap.keySet()) {
System.
out
.println("Head Set:" + key + "-"
+ headMap.get(key));
}
}
}


Ce programme a été testé avec Windows Vista, Java 1.6.0_04 le 22/03/2008



5 - Le comportement de Object.finalize()
Dans le programme de SCJP 6 il est écrit : "Given a code example, recognize the point at which an object becomes eligible for garbage collection, determine what is and is not guaranteed by the garbage collection system, and recognize the behaviors of the Object.finalize() method. "

En java, les objets en fin de vie sont gerés par le garbage Collection. C'est ce processus qui va detecter les objets à supprimer automatiquement. Le comportement du garbage collection respecte ces 3 regles :

- Un objet devient eligible au garbage collector quand il n'est plus referencé par un autre objet.
- Le system ne garantie pas quand la garbage collection sera appelé pour detruire l'objet, ni meme que l'objet sera detruit.
- Le system garantie que la methode finalize sera appelé une fois et une seul fois avant de detruire l'objet.


6- Liens
Le programme de SCJP 5 : http://www.sun.com/training/catalog/courses/CX-310-055.xml
Le programme de SCJP 6 :
http://www.sun.com/training/catalog/courses/CX-310-065.xml
Tutorial sur les certifications java :http://brabant.developpez.com/guide/certification/java
Javadoc de NavigationMap :
http://java.sun.com/javase/6/docs/api/java/util/NavigableMap.html
Javadoc de NavigationSet :
http://java.sun.com/javase/6/docs/api/java/util/NavigableSet.html
Tuto de rosindia sur NavigationSet : http://www.roseindia.net/java/jdk6/NavigableSetExample.shtml
Javadoc de la classe Console : http://java.sun.com/javase/6/docs/api/java/io/Console.html
Tutorial de Sun sur Console : http://java.sun.com/docs/books/tutorial/essential/regex/test_harness.html
Javadoc de object.finalize() :
http://java.sun.com/javase/6/docs/api/java/lang/Object.html#finalize
Doc en francais sur la memoire en java : http://gfx.developpez.com/tutoriel/java/gc/
Doc de sun sur le garbage collector : http://java.sun.com/docs/books/tutorial/java/javaOO/usingobject.html

Repost 0
Published by David Gimelle - dans Tutorial
commenter cet article
3 février 2008 7 03 /02 /février /2008 16:41


Voici un tutorial sur l'utilisation de Spring Ldap pour acceder aux données d'un serveur Ldap :

Introduction
L’objet de ce tutorial est de montrer comment accéder simplement aux données d’un serveur Ldap via Spring Ldap 1.2.
Il est n’est pas nécessaire d’utiliser ou de connaitre string pour utiliser ce tutorial.
Il n’est pas non plus nécessaire d’installer un serveur Ldap nous utiliserons le serveur public de www.openldap.com .

Vous trouverez les sources complètes de ce tutorial à la fin du document.

Installation d’un browser Ldap
Afin de pouvoir consulter le contenue du serveur Ldap, nous avons besoins d’un browser ldap. Je vous propose d’utiliser Softera Ldap Browser.
Télécharger et installer Softera Ldap Browser 2.6 sur le site de Softterra : http://www.ldapadministrator.com/download.htm. et en faire une installation complète.

Utiliser le browser pour aller sur le serveur préenregistrer de openLdap : www.openldap.com sur le port 389.
Ouvrir successivement les Nœuds :
OpenLdap / cn = DirectoryManager / ou=People .Sélectionner le Nœud avec le uid = Kurt.

Un clic droit dans les propriétés de Kurt permet de voir l’adresse complete de Kurt sur le serveur :ldap://www.openldap.com:389/uid=kurt,ou=People,dc=OpenLDAP,dc=Org

Cette adresse se compose de 2 parties :
Partie 1 : www.openldap.com:389 est l’adresse internet et le port du serveur ldap.
Partie 2 : uid=kurt,ou=People,dc=OpenLDAP,dc=Org est le Distinguish Name de l’enregistrement kurt. Il représente le chemin d’accès dans l’arbre ldap à kurt.

Maintenant que nous avons vu comment obtenir les informations sur Kurt via un Browser Ldap, nous allons faire la même chose avec du Java !

Téléchargement de Spring Ldap
Télécharger la bibliotheque spring-ldap-bin-with-dependencies-1.2.1.zip sur le site http://www.springframework.org/ldap
Décompresser cette archive.

Dans le repertoire dist se trouve spring-ldap-1.2.1.jar qui doit être placé dans le CLASSPATH du projet pour pourvoir faire fonctionner ce tutorial.

Dans le repepertoire lib se trouve commons-lang.jar,commons-loggin.jar,spring-beans.jar,spring-core.jar, toutes ces bibliotheques doivent aussi être ajoutées au CLASSPATH.


Création d’une connexion avec le serveur ldap.
Spring ldap utilise la notion de ContextSource pour définir une connection au serveur Ldap.
Créons une factory pour récupérer un ContextSource correct. ContextSource étant une interface, nous utiliserons LdapContextSource qui est une implémentation fournie par Spring Ldap

public class LdapContextSourceFactory {
public static ContextSource getLdapContextSource() throws Exception {
LdapContextSource ldapContextSource = new LdapContextSource();
ldapContextSource.setUrl("ldap://www.openldap.com:389");
ldapContextSource.setBase("dc=OpenLDAP,dc=org");
ldapContextSource.afterPropertiesSet();
return ldapContextSource;
}
}


Création de l’Objet Person
Spring Ldap permet de lire des enregistrements de personnes sous forme d’objet Person.

Voici notre class Person qui nous permettra d’obtenir une instance de Kurt. Uid est l’identifieur unique définit sur le serveur ldap pour les Person.

package com.neoneto.demo.springLdap1_2;
public class Person {
private String uid;
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
}


Récupération d’un objet Person via un DAO.
Il est possible d’utiliser le pattern DAO pour accéder aux objets Person. Voici comment procéder en 4 étapes

- 1 Créer un Mapper de Ldap vers Java
Un AttributMapper permet d’instancier correctement notre classe Person. C’est lui qui effectue le mappage entre la Structure Ldap Person et le model Objet Java Person.
Par exemple l’attribut Ldap «givenName» sera placé dans l’attribut Person.firstName de l’objet Java : p.setFirstName(attrs.get("givenName").get().toString());
Ce mapper est déclarée comme en class interne du DAO, car il doit pas être utilisé ailleurs que dans cette Dao.
public class PersonDao {
private static class PersonAttributMapper implements AttributesMapper {
public Person mapFromAttributes(Attributes attrs)
throws javax.naming.NamingException {
Person p = new Person();
p.setFirstName(attrs.get("givenName").get().toString());
p.setLastName(attrs.get("sn").get().toString());
p.setUid(attrs.get("uid").get().toString());
p.setEmail(attrs.get("mail").get().toString());
return p;
}
}
}

- 2 Utiliser un LdapTemplate
L’exécution de requêtes Ldap se fait via un LdapTemplate.
public class PersonDao {
….
private LdapTemplate ldapTemplate;
public void setLdapTemplate(LdapTemplate ldapTemplate) {
this.ldapTemplate = ldapTemplate;
}
….
}

- 3 Définir le Distinguish Name
Le Distinguish Name est le chemin d’accès à l’enregistrement de Person sur le serveur. La fonction BuildDn permet d’obtenir un Distinguish Name correcte.
public class PersonDao {
….
private Name buildDn(String uid) {
DistinguishedName dn = new DistinguishedName();
dn.add("ou", "People");
dn.add("uid", uid);
return dn;
}
….
}

- 4 Obtenir l’instance de la class Person
Il suffit d’utiliser le methode lookup du ldapTemplate pour obtenir l’instance correspondant a la clef primaire, c a d le uid dans le cas des Objet Person.
public class PersonDao {
….
public Person findByPrimaryKey(String uid) {
Name dn = buildDn(uid);
return (Person) ldapTemplate.lookup(dn, new PersonAttributMapper());
}
….
}


Test de la DAO
Créons une classe exécutable pour tester notre DAO.
public class TestLdap {
/** Retrieve a Kurt Person from ldap server and display Kurt in Standard Out */
public static void main(String[] args) {
}
}
Cette classe exécutable ne prend en compte aucun argument.
Ajoutons dans la méthode main le Context Source
// 1 Retrieve a LdapContextSource
ContextSource ldapContextSource = null;
try {
ldapContextSource = LdapContextSourceFactory.getLdapContextSource();
} catch (Exception e) {
System.out.println("Impossible to get a LdapContextSource.");
e.printStackTrace();
}
Il faut aussi obtenir un LdapTemplate.
// 2 Instanciate a LdapTemplate
LdapTemplate ldapTemplate = new LdapTemplate();
ldapTemplate.setContextSource(ldapContextSource);
Instancions un Dao et fournissons lui le Ldap Template.
// 3 instanciate a PersonDao
PersonDao dao = new PersonDao();
dao.setLdapTemplate(ldapTemplate);
La récupération de Kurt se fait simplement en utilisant la DAO.
// 4 retrieve a Person and display it
Person person = dao.findByPrimaryKey("kurt");
System.out.println("Uid: " + person.getUid());
System.out.println("FirstName: " + person.getFirstName());
System.out.println("LastName: " + person.getLastName());
System.out.println("Email: " + person.getEmail() + "n");
Lorsque nous exécutons cette classe nous obtenons dans le sortie standard :

Uid: kurt
LastName: Kurt
LastName: Zeilenga
Email: kurt@OpenLDAP.org

Ce qui correspond bien aux enregistrements contenue dans le serveur Ldap.


Amélioration de la DAO
Ajoutons une méthode dans la DAO qui permet de récupérer une liste de personne à partir de leurs noms de familles.

La classe LikeFilter permet d’utiliser des filtres de recherches avec des WildCard :
public List getPersonNamesByLastName(String lastName) {
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person"));
filter.and(new LikeFilter("sn", lastName));
return ldapTemplate.search("", filter.encode(),
new PersonAttributMapper());
}
Par exemple getPersonNamesByLastName(“E*”); retournera la liste des Personnes dont le noms de famille commence par E.
Complétons la classe main avec un exemple d’utilisation de cette nouvelle fonctionnalité.
// 5 retrieve a list of person
List listPerson = dao.getPersonNamesByLastName("*e*");
for (Object object : listPerson) {
System.out.println("Person: " + object);
}
L’exécution de la classe main retourne 2 personnes dans la sorties Standard.


Conclusion
Spring ldap est une librairie J2EE qui permet de récupérer simplement des enregistrements Ldap sous forme d’objets java et d’affranchir le développeur Java de la connaissance des mécanismes de Ldap.
Spring ldap permet aussi d’écrire sur le serveur Ldap. Reportez-vous à la documentation de référence de Spring Ldap qui explique très bien comment faire cela.


Liens et références
La doc de Spring Ldap :http://static.springframework.org/spring-ldap/docs/1.1.2/reference/
L’api de Spring Ldap : http://static.springframework.org/spring-ldap/docs/1.1.2/api/
En savoir plus sur Ldap : http://www-sop.inria.fr/semir/personnel/Laurent.Mirtain/ldap-livre.html
Liste de serveurs Ldap public : http://www.emailman.com/ldap/public.html
Blog de l'auteur : http:/getJ2ee.over-blog.com/

Contraintes techniques
Doc Version 1.0 - Publié le 17 Janvier 2008 par David Gimelle.
Ce tutorial a été testé avec Windows Vista, Eclipse 3.2 et le jdk 1.5.0_06  le 17 Janvier 2007.

Les Sources de ce tutorial :
Person.java :
package com.neoneto.demo.springLdap1_2;
public class Person {
private String uid;
private String firstName;
private String lastName;
private String email;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public String toString(){
return uid+" - "+firstName+" "+lastName;
}
}

2- PersonDao.java :
package com.neoneto.demo.springLdap1_2;
import java.util.List;
import javax.naming.Name;
import javax.naming.directory.Attributes;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.ldap.filter.LikeFilter;
public class PersonDao {
private LdapTemplate ldapTemplate;
private static class PersonAttributMapper implements AttributesMapper {
public Person mapFromAttributes(Attributes attrs)
throws javax.naming.NamingException {
Person p = new Person();
p.setFirstName(attrs.get("givenName").get().toString());
p.setLastName(attrs.get("sn").get().toString());
p.setUid(attrs.get("uid").get().toString());
p.setEmail(attrs.get("mail").get().toString());
return p;
}
}
public Person findByPrimaryKey(String uid) {
Name dn = buildDn(uid);
return (Person) ldapTemplate.lookup(dn, new PersonAttributMapper());
}
private Name buildDn(String uid) {
DistinguishedName dn = new DistinguishedName();
dn.add("ou", "People");
dn.add("uid", uid);
return dn;
}
public void setLdapTemplate(LdapTemplate ldapTemplate) {
this.ldapTemplate = ldapTemplate;
}
public List getPersonNamesByLastName(String lastName) {
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person"));
filter.and(new LikeFilter("sn", lastName));
return ldapTemplate.search("", filter.encode(),
new PersonAttributMapper());
}
}

3 – LdapContextSourceFactory.java :
package com.neoneto.demo.springLdap1_2;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.support.LdapContextSource;
public class LdapContextSourceFactory {
public static ContextSource getLdapContextSource() throws Exception {
LdapContextSource ldapContextSource = new LdapContextSource();
ldapContextSource.setUrl("ldap://www.openldap.com:389");
ldapContextSource.setBase("dc=OpenLDAP,dc=org");
ldapContextSource.afterPropertiesSet();
return ldapContextSource;
}
}

4 – TestLdap.java :
package com.neoneto.demo.springLdap1_2;
import java.util.List;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.LdapTemplate;
public class TestLdap {
/** Retrieve a Kurt Person from ldap server and display Kurt in Standard Out */
public static void main(String[] args) {
// 1 Retrieve a LdapContextSource
ContextSource ldapContextSource = null;
try {
ldapContextSource = LdapContextSourceFactory.getLdapContextSource();
} catch (Exception e) {
System.out.println("Impossible to get a LdapContextSource.");
e.printStackTrace();
}
// 2 Instanciate a LdapTemplate
LdapTemplate ldapTemplate = new LdapTemplate();
ldapTemplate.setContextSource(ldapContextSource);
// 3 instanciate a PersonDao
PersonDao dao = new PersonDao();
dao.setLdapTemplate(ldapTemplate);
// 4 retrieve a Person and display it
Person person = dao.findByPrimaryKey("kurt");
System.out.println("Uid: " + person.getUid());
System.out.println("FirstName: " + person.getFirstName());
System.out.println("LastName: " + person.getLastName());
System.out.println("Email: " + person.getEmail() + "n");
// 5 retrieve a list of person
List listPerson = dao.getPersonNamesByLastName("*e*");
for (Object object : listPerson) {
System.out.println("Person: " + object);
}
}
}

Repost 0
Published by David Gimelle - dans Tutorial
commenter cet article

Summary

  • : GetJ2ee -Java development
  • : Articles about Java and EE
  • Contact

Profil

  • David Gimelle
  • Java Developer for more 10 years. I worked in France, Canada and Switzerland. I am contractor in London since 2010.
  • Java Developer for more 10 years. I worked in France, Canada and Switzerland. I am contractor in London since 2010.

Contact

You can contact me here

or by Twitter

Search

Archives