Pour commencer, il faut créer une nouvelle classe avec un constructeur par défaut et une méthode ayant les caractéristiques suivantes :

  • Un paramètre de type javax.interceptor.InvocationContext
  • Un retour de type Object
  • Annotée avec javax.interceptor.AroundInvoke
public class ExceptionInterceptor
{
    public ExceptionInterceptor(){}
 
    @AroundInvoke
    public Object invokeMethod(InvocationContext context) throws Exception
    {
        try
        {
            return context.proceed();
        }
        catch (Exception e)
        {
            //encapsulation de l'exception dans une autre
            throw new Exception(e.getMessage(), e);
        }
    }
}

Du coup, plutot que d'invoquer directement une méthode sur un EJB, le serveur va passer par cet intercepteur qui provoquera l'exécution en appelant la méthode proceed. Dès lors, si une exception est catchée, elle sera encapsulée dans une nouvelle exception qui elle remontera jusqu'à la facade (cf mon billet précédent).

Il suffit ensuite de préciser sur quels EJB cette intercepteur doit être utilisé. Cela se fait en utilisant l'annotation @Interceptors :

@Stateless
@Interceptors(ExceptionInterceptor.class)
public class ProcessorBean implements ProcessorLocal
{
   public String process()
   {
      throw new RuntimeException("error");
   }
}

Au final, plus de ejb.some_unmapped_exception ! Par contre cela peut créer certaines failles de sécurité, notamment si l'exception remonte jusqu'au client puisque la stack trace sera contenue dans l'objet. Dans mon cas, toutes les exceptions étant traitées au niveau de la facade WebService, aucun problème de ce coté-là.

J'ai mis le projet de démonstration (fait avec NetBeans) en annexe de ce billet :)

[Mise à jour du 05.05.2009]
Voila une deuxième annexe avec un projet NetBeans beaucoup plus simple à utiliser pour voir le comportement des RuntimeException !