mardi 5 février 2013

Démarrer une base de données h2 avec Maven

Pour travailler sur un projet un local, c'est bien pratique de pouvoir ne pas dépendre d'une base de données MySQL externe.

Une solution c'est de démarrer une base de données de type H2 ou HsqlDb.

Pour démarrer une base de données H2, il faut utiliser une le plugin exec-maven-plugin .

Je vous conseille d'ajouter le plugin dans un profile, on a fréquemment besoin du plugin exec plus d'une fois dans un projet.



Il faut ajouter la dépendance à H2.

J'ai setté la variable local-h2-directory par local-ignore, ça évite que les fichiers h2 soient supprimés quand vous faites un mvn clean.

L'argument -tcp indique que la connexion à la bdd se fait via tcp, vous pouvez aussi côté client, spécifier une connexion par fichier, ça évite de devoir démarrer la bdd h2, mais dans ce cas, une seule application pourra se connecter à la bdd. En mode tcp, plusieurs jvm peuvent accéder à la bdd.

Pour initialiser la base, utilisez sql-maven-plugin :



Par défaut, le login d'accès à une base h2 est "sa" et il n'y a pas de mot de passe, l'adresse jdbc : jdbc:h2:tcp://localhost/bdd;MODE=MySQL. Le paramètre ";MODE=MySQL" indique au driver de "parler" le sql en mode MySQL.

Je n'ai pas eu trop de souci, sur le projet dans lequel je l'ai rajouté, mais H2 n'est pas 100% compatible avec MySQL. Les vues par exemple ne sont pas gérés, et des paramètres sql trop spécifique ne fonctionne pas forcément de la même façon.

lundi 6 juin 2011

Comment tracer l'appel à une méthode en utilisant l'AOP, Guice et Slf4j

 Je trouve l'annotation @Log de Tapestry, très pratique. Et il est possible de faire la même chose avec Guice.

Il y a un article qui explique les bases de l'AOP avec Guice ici.

Il faut ces dépendances :

  • Guice, pour le moteur d'injection, et qui permet également de faire de l'AOP.
com.google.inject
guice
3.0

  • Slf4j, comme moteur de logging (avec logback en l'occurence comme implémentation).
  org.slf4j
  slf4j-api
  1.6.1
  • AOP, pour l'interface MethodInterceptor
  aopalliance
  aopalliance
  1.0

On commence par créer une annotation : 

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log {
}


On créait l'implémentation de l'interception avec la classe LogInterceptor : 

public class LogInterceptor implements MethodInterceptor {
     public Object invoke(MethodInvocation invocation) throws Throwable {

     }
}


Et on fait l'association dans le module Guice :

public class MyModule extends AbstractModule {
    @Override
    protected void configure() {
        bindInterceptor(Matchers.any(), Matchers.annotatedWith(Log.class),
          new LogInterceptor());
    }
}



Une contrainte est que bien évidemment la méthode sur laquelle on rajoute cette annotation doit faire parti d'une classe elle-même injectée par Guice.

Et rajouter l'annotation sur la méthode à tracer : 

public class MyService {
    @Log
    public String getAvailableCode(Code code) {
        switch (code) {
        case FIRST:
            return "my first code";
        case SECOND:
            return "my second code";
        default:
            return "null";
        }
    }
}



Maintenant on passe à l'implémentation de la classe LogInterceptor, on commence par récupérer le logger de la classe de la méthode tracée :


public class LogInterceptor implements MethodInterceptor {
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        Class declaringClass = method.getDeclaringClass();
        Logger logger = LoggerFactory.getLogger(declaringClass);
    }
}



Et avant de tracer quelquechose, on vérifie si le niveau de log est activé, sinon on renvoie directement le résultat : 

if (!logger.isDebugEnabled()) {
    return invocation.proceed();
}


Sinon on trace l'appel est les paramètres passés à la méthode : 

StringBuilder sb = new StringBuilder();
sb.append(method.getName()).append("()").append("[ENTER]");
Object[] arguments = invocation.getArguments();
if (arguments != null && arguments.length != 0) {
    sb.append("[");
    for (int i = 0; i < arguments.length; i++) {
        sb.append("[argument(").append(i).append(")=\"")

          .append(arguments[i]).append("\"]");
    }
    sb.append("]");
}
logger.debug(sb.toString());


Et le résultat de la méthode :

Object proceed = invocation.proceed();
if (profile) {
    stopWatch.stop();
}
sb = new StringBuilder();
sb.append(method.getName()).append("()").append("[EXIT ]");
sb.append("[return=\"").append(proceed).append("\"]");
if (profile) {
    sb.append(" in ").append(stopWatch.getElapsedTime()).append(" ms");
}
logger.debug(sb.toString());
return proceed;


Et dans les logs :

15:48:42.382 [main] DEBUG net.antoine.aopguice.MyService - getAvailableCode()[ENTER][[argument(0)="FIRST"]]
15:48:42.424 [main] DEBUG net.antoine.aopguice.MyService - getAvailableCode()[EXIT ][return="my first code"]

Après le premier problème sur lequel on peut tomber est le cas où la méthode renvoie une exception comme par exemple ici :


@Log
public void throwException() {
    throw new IllegalArgumentException("just for test");
}


Dans ce cas, on catche l'erreur, logue l'exception et on la renvoie :

Exception exception = null;
Object proceed = null;
try {
    proceed = invocation.proceed();
} catch (Exception e) {
    exception = e;
} finally {
    sb = new StringBuilder();
    sb.append(method.getName()).append("()").append("[EXIT ]");
    if (exception == null) {
        sb.append("[return=\"").append(proceed).append("\"]");
    } else {
        sb.append("[throws exception :").append(exception.toString())
          .append("]");
    }
    logger.debug(sb.toString());
    if (exception != null) {
        throw exception;
    }
}
return proceed;


Ce qui donne dans les logs :

16:01:04.594 [main] DEBUG net.antoine.aopguice.MyService - throwException()[ENTER]
16:01:04.603 [main] DEBUG net.antoine.aopguice.MyService - throwException()[EXIT ][throws exception :java.lang.IllegalArgumentException: just for test]

Maintenant un élément intéressant serait de tracer le temps d'éxécution de la méthode, on va utiliser pour celà Perf4j : 
  
  org.perf4j
  perf4j
  0.9.14

Idéalement, on aimerait que ce soit une option désactivé par défaut puisque inutile dans la plupart des cas. On rajoute une attribut à l'annotation : 


public @interface Log {
    /**
    * Active timing execution if set to true, false by default.
    */
    boolean profile() default false;
}


Et on va récupérer ce paramètre dans la méthode d'interception : 

Log annotation = method.getAnnotation(Log.class);
boolean profile = false;
if (annotation != null) {
    profile = annotation.profile();
}
StopWatch stopWatch = null;
if (profile) {
    stopWatch = new LoggingStopWatch(method.getName());
}

if (profile) {
    stopWatch.start();
}
Object proceed = null; 

try { 
    proceed = invocation.proceed(); 
} catch (Exception e) { 
    exception = e; 
} finally { 
    if (profile) { 
        stopWatch.stop(); 
    }
    //etc
    if (profile) {
        sb.append(" in ").append(stopWatch.getElapsedTime())
          .append(" ms");
    }
    //etc
}



Et avec cette méthode :  

@Log(profile = true)
public void traceTime() throws InterruptedException {
    Random random = new Random();
    int nextInt = random.nextInt(100);
    Thread.sleep(50 + nextInt);
}


On obtient ces logs : 

16:22:11.759 [main] DEBUG net.antoine.aopguice.MyService - traceTime()[ENTER]
start[1307283731759] time[117] tag[traceTime]
16:22:11.877 [main] DEBUG net.antoine.aopguice.MyService - traceTime()[EXIT ][return="null"] in 117 ms

Après on pourrait rajouter un attribut dans l'annotation pour pouvoir loguer en info, on rajouter un message à rajouter dans les traces. N'hésitez pas à rajouter un commentaire si vous avez d'autres idées.

mercredi 2 décembre 2009

Cirque Éloize - Rain, cette semaine au Colisée de Roubaix


"Rain", c'est le nom du spectacle en représentation ce soir au Colisée de Roubaix. Ce spectacle, c'est encore son metteur en scène, Daniele Finzi Pasca qui en parlera le mieux :
J'ai grandi dans une monde où le souvenir était fait d'instants, d'impossibles immobilités et d'images volées au temps. Moi, dans mes spectacles, j'ai simplement mis en mouvement ces images.
C'est le genre de spectacle qui ne vous laisse pas le souvenir d'une histoire, mais le souvenir des émotions qu'on y a ressenti : de la poésie, de la beauté. C'est un mélange de la virtuosité du Cirque du soleil, de la narration d'un Ghibli, et de l'imagination d'un film de Jeunet.
A voir donc absolument, le Cirque Éloize est encore en représentation, ce jeudi et vendredi 3,4 décembre. Sachant que le colisée fait des places "Dernière Minute" à 8 euro, et qu'il restait des places ce soir. Ce serait une injustice que les 2 prochaines dates ne soient pas complètes : pitié précipitez-vous !!

Flickr - Mario Groleau - cc