lundi 17 juin 2013

Les EJB et le classpath

Lorsqu'on créer un EJB dans un EAR, l'EJB a le même classpath que l'EAR qui le contient.
Voici un schéma représentant les classpaths :

Mais si l'EJB nécessite des dépendances qui ne sont pas dans ce classpath, comment faire alors pour fournir ces dépendances dans l'EAR et que seul l'EJB ai ces dépendance dans son classpath ?

La solution va être fournit par le plugin maven jar.
Celui-ci dispose d'une option addClasspath qui permet d'enrichir le fichier manifest du jar (MANIFEST.MF) afin d'ajouter explicitement des jars dans le classpath.
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-jar-plugin</artifactId>
  ...
  <configuration>
    <archive>
   <manifest>
     <addClasspath>true</addClasspath>
   </manifest>
    </archive>
  </configuration>
  ...
</plugin>
Le manifest ressemble alors à ceci :
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: ${user.name}
Build-Jdk: ${java.version}
Class-Path: plexus-utils-1.1.jar commons-lang-2.1.jar
Cette méthode n'est pas forcément très élégante. Mais puisque les dépendances sont dans l'EAR, cela dépanne avec un moindre impacte (pas de création de SharedLib en plus sous WebSphere par exemple).

lundi 3 juin 2013

Connaitre le type d'une liste genérique par introspection

En Java, il est possible de faire de l'introspection, c'est a dire de regarder à l'intérieur de l'objet.
Cela permet par exemple, de connaitre les champs privés ou public, les méthodes...

Imaginons maintenant, que vous fassiez de l'introspection sur un objet avec le code suivant :
objetInspecte = ....

// Tout les champs declarer, y compris prive
Field[] fields = objetInspecte.getClass().getDeclaredFields() ;
Class generiqueClass ;
// Valeur du champs introspecte
Object valeurDuChamps ;

for(Field field : fields) {
    // Rend le champ accessible et lecture/ecriture
    field.setAccessible(true);
                
    valeurDuChamps = field.get(objetInspecte) ;
}

Oui, mais voilà, vous avez un liste, de ce fait field.getType().getCanonicalName() renvoie java.util.List.
Très bien, maintenant vous pouvez faire un new ArrayList pour créer la liste si le champs est null.

A présent, vous souhaitez, dans cette liste ajouter un élément. Mais de quel type est cette liste ? String, Integer, un tout autre objet ?
Pour cela, Java fournit une API. Voici un exemple :
// Type pour les generique
ParameterizedType pt ;  
// Class generique
Class generiqueClass ;

// On recupere le type generique
pt = (ParameterizedType) field.getGenericType() ;
generiqueClass = (Class) pt.getActualTypeArguments()[0];

La valeur de generiqueClass ? Et bien nous découvrons, que c'est une liste de Date par exemple !
Pour créer un objet, rien de plus simple : generiqueClass.newInstance().

lundi 27 mai 2013

Modifier le rendu d'une liste de string avec XStream en utilisant un converter

Continuons avec la classe Toto contient une liste de String dont le rendu est le suivant :
<liste__Produit>
  <string>truc</string>
  <string>bidule</string>
</liste__Produit>

Plutôt que d'avoir une suite de balise string, il serait souhaitable d'avoir une balise part.
Dans la documentation, il existe l'annotation @XStreamImplicit.
Il y a deux paramètres : itemFieldName et keyFieldName.
Le premier indique l'alias de la collection et le deuxième sert pour les type Map.
Avec le code suivant :
@XStreamAlias("listeProduit")
@XStreamImplicit(itemFieldName="part")
public List titi = new ArrayList() ;

Le résultat est à présent :
<produit>
  <id>1</id>
  <part>truc</part>
  <part>bidule</part>
  <dateEnvoie>2013-03-01 10:12:04.306 UTC</dateEnvoie>
</produit>

La liste est donc mise à plat avec une suite d'entrée.
Si il est désiré d'avoir une balise parente, il va falloir passer par un converter maison.
Voici l'annotation :
@XStreamAlias("listeProduit")
@XStreamConverter(value=ListToStringXStreamConverter.class, strings={"part"}) 
public List titi = new ArrayList() ;

Avant de continuer, voyons un peu le principe.
value indique le nom de la classe de conversion.
strings est quant à lui disponible dans l'annotation pour des besoins annexes. Ce paramètre sera transmis au constructeur. D'autres champs sont mis à disposition et peuvent être exploités pour des besoins spécifiques comme types qui permet de contenir des classes.
// Come from http://stackoverflow.com/questions/1791178/customising-serialisation-of-java-collections-using-xstream
public class ListToStringXStreamConverter implements Converter {

private String alias;

public ListToStringXStreamConverter(String alias) {
    super();
    this.alias = alias;
}

@SuppressWarnings("rawtypes")
@Override
public boolean canConvert(Class type) {
    return true;
}

@Override
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {

    @SuppressWarnings("unchecked")
    List list = (List)source;

    for (String string : list) {
        writer.startNode(alias);
        writer.setValue(string);
        writer.endNode();
    }
}

@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
    throw new UnsupportedOperationException("ListToStringXStreamConverter does not offer suport for unmarshal operation");
}

Et mainteant, voici le rendu :
<produit>
  <id>1</id>
  <listeProduit>
    <part>truc</part>
    <part>bidule</part>
  </listeProduit>
  <dateEnvoie>2013-03-01 10:25:19.10 UTC</dateEnvoie>
</produit>

Ce qui est fait, c'est de remplacer le composant qui affiche une liste par le notre.
Par défaut, XStream écrit l'alias du champs et ensuite le converter écrit les données dans le flux. Pour cela, il prend en paramètre le nom du noeud à créer ("part").

lundi 20 mai 2013

Modifier le rendu d'une balise XML avec XStream en utilisant un converter

Toujours en utilisant le même exemple, la classe Toto contient une date dont le rendu est le suivant :
<date__Envoie>2013-02-28 17:01:29.669 UTC</date__Envoie>

Afin de personnaliser l'affichage de la date, il est possible via une annotation d'indiquer a XStream de ne pas utiliser le rendu standard mais un rendu personnalisé.
Il est donc nécessaire d'ajouter l'annotation suivant au champ dateEnvoie (en-dessous de l'annotation alias) :
@XStreamConverter(value=MyDateConverter.class)

Dans le cas d'une date (mais aussi pour une String, Integer...), il s'agit de remplacer le comportement de la classe com.thoughtworks.xstream.converters.basic.DateConverter.
Cette classe hérite de la classe com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter.
Il est simplement nécessaire de redéfinir les méthodes :
public class MyDateConverter extends AbstractSingleValueConverter {
    // Indique si cette classe prend en charge la conversion
    @Override
    public boolean canConvert(Class type) {
        return type.equals(java.util.Date.class);
    }

    // Transforme la valeur XML en Java
    @Override
    public Object fromString(String str) {
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd") ;
        
        Date dateStr = null ; 
            
        try {
            dateStr = sdf.parse(str) ;
        } catch (ParseException e) {
            // Nothing
        }
         
        return dateStr ;
    }
    
    // Transforme la valeur Java en XML
    @Override
    public String toString(Object obj) {
        final Date date = (Date)obj;
        
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd") ;
        
        return sdf.format(date) ;
    }
}

vendredi 17 mai 2013

Tutoriel MyBatis

Vous cherchez une alternative à Hibernate ?
Vous souhaitez utiliser un ORM plus simple à mettre en oeuvre ?

Alors essayez MyBatis et pour ce faire, vous trouverez sur ce blog un tutoriel complet (voir les liens à côté).

lundi 13 mai 2013

Changer la convention des noms des balises avec XStream (exemple avec les underscores)

Afin de ne pas avoir de double underscore ("_"), il est nécessaire d'indiquer à XStream la convention de nommage des balises.
Cela ce fait à la création de l'objet XStream :
XStream stream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_")));

lundi 6 mai 2013

Changer le nom des balises XML avec XStream (utilisation des alias)

Il est possible via des annotations de changer le nom de la balise dans le flux XML.
En reprenant la classe Toto :
import com.thoughtworks.xstream.annotations.XStreamAlias;

@XStreamAlias("produit")
public class Toto {
  @XStreamAlias("id")
  public String toto = "1";
  
  @XStreamAlias("liste_Produit")
  public List titi = new ArrayList() ;
  
  @XStreamAlias("date_Envoie")
  public Date tata = new Date() ;

Il est nécessaire d'indiquer à XStream de prendre en compte les annotations, en ajoutant, avant de générer le rendu, la ligne suivante :
stream.autodetectAnnotations(true) ;

Voici le nouveau rendu :
<produit>
  <id>1</id>
  <liste__Produit>
    <string>truc</string>
    <string>bidule</string>
  </liste__Produit>
  <date__Envoie>2013-02-28 17:01:29.669 UTC</date__Envoie>
</produit>
Vous remarquerez peut-être que les alias avec un underscore ("_"), voit celui-ci doublé.
La raison est expliquer dans la FAQ de XStream.