Archive

Archive for août 2009

Télé

Monster_Garage_54J’ai découvert et adoré  l’émission de télé « Monster Garage« , (AB moteurs, vers 22h30 le soir). Même si c’est de la télé-réalité, ce show réussit à être à la fois divertissant et instructif, pour un peu qu’on ait un peu d’intéret pour la mécanique…

Le concept de « télé-réalité professionelle » est excellent, et permet vraiment de s’imprégner de l’ambiance d’un métier d’une manière divertissante.

Dans le même genre je vous conseille « Cauchemar en cuisine » avec Gordon Ramsey sur W9.

Publicités
Catégories :Uncategorized Étiquettes : , ,

A bas les procédures stockées

Juste pour vous faire pointer un excellent articles d’un non moins excellent blog : Il explique acoding-horror-official-logo-small quel point les procédures stockées sont une plaie, et pourquoi tout projet digne de ce nom devrait pouvoir s’en passer. C’est génial : tous les arguments que j’ai pu fournir au fil des années dans les pires trolls de bureau sont réunis, expliqués et classés  🙂

Who Needs Stored Procedures, Anyways?

Catégories :Uncategorized Étiquettes : , , ,

Trop lourd

19 août 2009 1 commentaire

Aujourdh’ui j’ai démarré dans le cadre de mon boulot un projet web… Ajax. Bien que j’ai toujours trouvé TRES discutable d’utiliser les technologies du web pour construire des vraies applications de gestion, j’en ai fait mon métier…

j’architecture donc le machin : une appli pour conteneur de servlet (tomcat) :

accés bdd gérés par ibatis ( l’appli fonctionne avec une ancienne base… sinon j’aurais bien poussé openJPA…)

  • – spring 2.5
  • – icefaces 1.8 (par dessus myfaces 1.2)
  • – facelets

La maquette de l’appli tient dans un war de 12 Mo !!!!. La, je marque un arrêt :

C’est INCROYABLE a quel point les développement j2ee sont bloatées ! c’est effrayant. A force de d’ajouter des frameworks de frameworks d’injection d’ajax de webservice d’orm de mes deux, on n’arrive même plus a envisager de démarrer le moindre développement sans au moins 10 Mo de jars…

Revenons a la simplicité, pitié, pourquoi toujours ajouter des solutions compliquées pour régler des problèmes simples ?

Ou sont les demos 64k de notre enfance ? (64 Ko, vous vous rendez compte !)

Imaginez que visicalc (en tout cas sa version pour pc),  un logiciel tableur complet, tient dans un executable de 27,5 ko !
Sérieusement, je pense que l’on est allé beaucoup trop loin dans la course à l’armement.

Au lieu de normaliser des solutions élégantes, légères et fiables, l’état de l’art nous impose d’essayer continuellement de réaliser l’irréalisable avec des technologies (ici, l’html) qui ne sont pas faites pour ça !

C’est tellement grotesque qu’on ne se rend même plus compte :

« un clic sur un bouton (balise html) va provoquer l’éxecution d’un javascript qui, gràce à une extension « plus ou moins disponible selon le navigateur (xmlhttp), va effectuer une requete HTTP, vers un serveur d’application (java). Un traitement est effectué, (peut-être même un peu de SQL, ou un appel a un webservice), avant de répondre a la requete par un document JSON (type mime application/json) , qui sera a son tour parsée et traitée par du javascript pour modifier le html »

Bien sur, a chaque que l’on passe d’un domaine a l’autre, on se repose les mêmes questions, et a chaque la roue a été inventée d’une manière différente : conversion de format, modèle conceptuel, sécurité, confidentialité, cache, accés concurrents…. sans compter les bugs et les spécifités des : navigateurs/proxy/routeur/serveur/base de donnée/os.

Est-ce que vous pensez vraiment que toute cette chaine est conceptuellement intelligible, et saine .Pensez vous que c’est une bonne manière de mettre en oeuvre ses besoins logiciels ?

Catégories :Uncategorized Étiquettes : , , ,

Spring, aop, caches…

1237036892[reprise d’un ancien article : j’ai vu depuis des librairies mieux écrites pour faire le meme genre de choses, mais cet article reste intéressant si vous voulez comprendre des mécanismes des annotations java, et un vrai exemple de programmation orientée aspect (AOP) ]

Introduction

Annotations

Les annotations java5 permettent d’ajouter des méta-données au classes, et déclarations de méthode. Ces informations peuvent être disponible au runtime. On peut savoir programmatiquement si telle ou telle méthode comporte telle ou telle annotation.

C’est une manière syntaxique élégante pour enrichir la définition des classes, et leur ajouter des informations non prévues dans le modèle de programmation propre a java.

AOP

L’AOP (aspect-oriented programming) est un paradigme de programmation permettant l’injection de code supplémentaire (‘advice’) par dessus un programme existant (‘subject’), de manière a implémenter de nouvelles fonctionnalités (‘concern’).

Le principe est de cibler les évenement d’un programme (appel d’une méthode, instanciation d’un objet, etc..) et de permettre l’execution de code arbitraire sur ces évenements. On utilise l’AOP pour ajouter des fonctions transverses a un programme sans modifier tout le code, et en évitant de mélanger des activités différentes dans un même code (typiquement : la sécurité, le logging, le tracage,…)

Spring fournit en interne une gestion de l’AOP, via la librairie AspectJ

Je me propose d’utiliser ces techniques pour implémenter une gestion automatique de cache.

principe de cache

Pour moi cache est un objet permettant de stocker des couples clé / valeur de manière a éviter de réeffectuer un traitement long.

Il s’agit donc de trouver une manière simple pour décrire qu’une méthode quelconque peut utiliser un cache, c’est a dire stocker/retrouver des résultats de manière transparent au lieu d’effectuer un traitement lors de chaque appel.

Interfaces

Je défini donc l’interface d’un cache :

public interface Cache {
      public void put(String key, Object valueToStore);
      public CacheResult get(String key);
}

et

public interface CacheResult {
     public boolean isAvailable();
     public String getKey();
     public Object getData();
}

partant ces ces deux interfaces, j’ai implémenté plusieurs types de cache (je ne copie pas le code ici, mais je peux vous envoyer un zip si vous demandez gentillement…)

  • une implémentation « dummy » qui ne stocke rien, et renvoie tout le temps des CacheResult « pas trouvé »
  • une implémentation « maison », la classe Cache ne fait qu’embarquer une hashmap.
  • une implémentation basée sur memcached, en utilisant la librairie de http://www.whalin.com/memcached/
  • une implémentation basée sur EHCache.

Annotation

Je définit une annotation : celle ci me permettra de « marquer » les méthodes :

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;

@Documented
@Retention(RetentionPolicy.RUNTIME) //annotation conservée a l'execution
@Target(ElementType.METHOD) // l'annotation s'applique aux méthodes
public @interface UseCache {

String cacheName(); //cacheName est le parametre de l'annotation
}

Vous remarquerez a quel point il est simple de créer ses propres annotations !
La syntaxe « cacheName(); » paraitra un peu bizarre pour aux vieux briscard du java, mais c’est bien la syntaxe qui permet d’ajouter des paramètres aux annotions…

appliquer l’annotation par AOP

Annoter son code c’est bien, faire en sorte que ça fasse des trucs, c’est mieux ! J’utilise aspectJ + Spring pour associer a mon annotation « UseCache » un comportement spécifique au runtime.

Une annotation spécifique permet de déclarer la classe qui va se charger d’éxecuter du code quand on tente d’éxecuter les méthodes annotées « useCache » :

@Aspect
public class CachingInterceptor implements ApplicationContextAware {

private static final Log _logger = LogFactory.getLog(CachingInterceptor.class);

private ApplicationContext applicationContext;

@Around("@annotation(net.jr.caching.annotation.UseCache)")
public Object aroundCachedMethod(ProceedingJoinPoint jp) {
// find the cache, and the called method.
Method invoked = getMethod(jp);
Annotation annotation = invoked.getAnnotation(UseCache.class);
String cacheKey = Integer.toHexString(invoked.hashCode()) + "_"
+ buildCacheKey(jp.getArgs());

_logger.debug("the cache key for this call is "+cacheKey);

Cache cache = (Cache) getApplicationContext().getBean(
((UseCache) annotation).cacheName());

// try to retrieve data from cache
if (cache != null) {
CacheResult result = cache.get(cacheKey);
if (result.isAvailable()) {
_logger.debug("cache hit !");
return result.getData();
}
}

// no data available, so we call the original method.
_logger.debug("no data found in cache for key "+cacheKey+", calling original method code...");
try {
Object result = jp.proceed(jp.getArgs());
if (cache != null) {
_logger.debug("... putting method call result in cache with the key "+cacheKey);
cache.put(cacheKey, result);
}
return result;
} catch (Throwable ite) {
throw new RuntimeException(ite);
}
}

protected String buildCacheKey(Object... objs) {
int hash = 0;
for (Object arg : objs) {
hash ^= arg.hashCode();
}
return Integer.toString(hash);
}

@SuppressWarnings("unchecked")
protected Method getMethod(JoinPoint jp) {
MethodSignature met = (MethodSignature) jp.getSignature();
try {
Method method = jp.getSourceLocation().getWithinType().getMethod(
met.getMethod().getName(),
met.getMethod().getParameterTypes());
method.setAccessible(true);
return method;
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}

protected Cache getCache(String name) {
return (Cache) getApplicationContext().getBean(name);
}

@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}

protected ApplicationContext getApplicationContext() {
return applicationContext;
}

Ce code mérite quelques explications :

Création de l’Advice

l’advice est l’execution de la méthode « aroundCachedMethod »

l’execution consiste

  1. a trouver le cache dans l’applicationContext d’aprés sont nom,
  2. A frabriquer une ’empreinte’ (xor entre les hashcodes) a partir de la combinaison des paramètres d’entrée de la méthode « wrappée »
  3. chercher dans le cache la valeur stockée pour cette empreinte, la renvoyer directement quand elle est trouvée
  4. sinon, éxecuter la méthode originale et stocker la valeur de retour dans le cache.

Définition des pointcuts

les pointcuts ( = expression définissant le type d’évenement a surveiller) est également définit par annotation !

@Around("@annotation(net.jr.caching.annotation.UseCache)")

Veut dire  » A la place de (toute méthode comportant l’annotation ‘UseCache’)  »

Une documentation complète sur la syntaxe des pointcuts est disponible sur le site d’AspectJ

Intégration dans Spring

Dans le fichier de conf Spring, il faut déclarer l’utilisation de l’aop :

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

    <aop:aspectj-autoproxy proxy-target-class="false" />

....

Puis on déclare notre « Interceptor » :

<bean id="cachingInterceptor" />

Puis on déclare un cache :

<bean id="monCache" />

C’est magique, Il suffit maintenant dans le code d’utiliser l’annotation pour qu’une méthode tire partie de notre cache :

class MyClass
{
@UseCache(cacheName="monCache")
     public String rechercheCompliquee(String criteres) {
          ....
     }
}

CONCLUSION :

cette méthode d’application de vos annotations vous permet d’appliquer des aspects sans avoir a modifier la manière dont le code est compilée. L’intégration avec Spring est également appréciable.

Par contre, faites attentions a l’impact sur les performances de ce type d’aop. Je me pose également des questions dans le cas ou un autre système de modification de bytecode (par exemple hibernate) est dèja en place…

Catégories :Uncategorized Étiquettes : , , ,