Diferencia entre revisiones de «Excepciones Seguras De Deserializar»

De Dos Ideas.
Saltar a: navegación, buscar
 
(No se muestran 10 ediciones intermedias de 3 usuarios)
Línea 1: Línea 1:
Cuando se expone un método para ser invocado en forma remota, por ejemplo en un EJB, puede suceder que tire una excepción como respuesta a la invocación como consecuencia de algún error durante la ejecución. Esta excepción será serializada y pasada por red al método que realizó la invocación.
+
[[Category:Java]]
 +
En [[Java]], cuando se expone un método para ser invocado en forma remota, por ejemplo en un [[EJB]], puede suceder que tire una excepción como respuesta a la invocación como consecuencia de algún error durante la ejecución. Esta excepción será serializada y pasada por red al método que realizó la invocación.
  
 
Cuando llega la excepción del otro lado tiene que volver a hidratarse para lo que es necesario contar con la definición de la clase a deserializar.
 
Cuando llega la excepción del otro lado tiene que volver a hidratarse para lo que es necesario contar con la definición de la clase a deserializar.
Línea 5: Línea 6:
 
Con un poco de cuidado y haciendo las cosas bien, la clase de la Excepción existirá del lado del cliente. Pero también debemos tener en cuenta que las excepciones suelen tener anidada la causa que las produjo (otra excepción) y la causa puede tener anidada a su vez otra causa y así sucesivamente hasta llegar a la raíz del problema que originó el fallo.
 
Con un poco de cuidado y haciendo las cosas bien, la clase de la Excepción existirá del lado del cliente. Pero también debemos tener en cuenta que las excepciones suelen tener anidada la causa que las produjo (otra excepción) y la causa puede tener anidada a su vez otra causa y así sucesivamente hasta llegar a la raíz del problema que originó el fallo.
  
Todas las clases de la cadena de causas deben estar presente del lado del cliente que invoca el método remoto ya que intentará rehidratar toda la cadena.
+
Todas las clases de la cadena de causas deberá estar presente del lado del cliente que invoca el método remoto ya que intentará rehidratarlas. A veces, por alguna razón u otra, puede ser que no tengamos disponible las excepciones de alguna parte de la cadena de causas del lado del cliente. Deberíamos intentar solucionar este problema investigando porqué no están esas excepciones e incluirlas.
  
A veces, por alguna razón u otra, puede ser que no tengamos disponible las excepciones de alguna parte de la cadena en el cliente. Este ejemplo muestra como reemplazar las excepciones de la cadena por excepciones que estamos seguros que pueden ser hidratadas nuevamente.
+
Como último recurso, si no fuera posible asegurar su existencia del lado del cliente, este ejemplo muestra como reemplazar las excepciones de la cadena por excepciones que estamos seguros que pueden ser hidratadas nuevamente.
  
=== En el método que se invocará remotamente ===
+
=== Objetivo ===
 +
El objetivo es eliminar el problema de la rehidratación de una excepción del lado del cliente con la mínima pérdida de información posible.
  
public class Xxx {
+
Una solución rápida sería cortar la cadena de causas, pero esto nos dejaría ciegos frente a un problema.
  
     public void metodoRemoto() throws RemoteException {
+
Otra solución sería arrojar una excepción que copie los stack trace de todas sus causas y organice material descriptivo, pero dejaría la información inaccesible con tratamientos estándar.
 +
 
 +
Finalmente se plantea reemplazar las excepciones de la cadena de causas con excepciones que estamos seguros que se pueden rehidratar del lado del cliente y se copia la información del mensaje y el stack trace de las causas originales.
 +
 
 +
Con esta solución, se sigue recibiendo excepciones encadenadas y se conserva el stack trace en cada una de ellas en forma estándar.
 +
 
 +
El costo que tiene es que se pierde la clase original de la excepción que se reemplace. Es por esto que debe utilizarse a conciencia.
 +
 
 +
=== Posible mejora ===
 +
El ejemplo que se plantea hace un reemplazo masivo de las causas en el último momento posible. Una mejora importante sería discriminar que excepciones pueden no ser reemplazadas para conservar intacta la mayor parte posible de la cadena.
 +
 
 +
=== Escenario ===
 +
Una clase Blah que tiene un métodoX que se invocará remotamente.
 +
* Se envuelve su funcionamiento en un bloque try para atajar las excepciones que se produzcan.
 +
* En el catch se reemplaza la excepción junto con su cadena de causas por excepciones que es seguro que se pueden rehidratar del lado del cliente.
 +
 
 +
<code java>
 +
public class Blah {
 +
 
 +
     public void metodoX() throws RemoteException {
 
         try {
 
         try {
           ...
+
           //Lo que hace el metodoX
          todo el funcionamiento debe pasar dentro del try
 
          para atajar cualquier excepcion que pueda pasar
 
 
         } catch (Exception e) {
 
         } catch (Exception e) {
          //se relanza una excepcion a la que se le reemplaza toda la
 
          //cadena de causas por RuntimeExceptions.
 
 
           throw HidratacionSegura.asegurarComoRuntime(e);
 
           throw HidratacionSegura.asegurarComoRuntime(e);
 
         }
 
         }
Línea 26: Línea 43:
  
 
}
 
}
 +
</code>
  
 
+
=== Reemplazo recursivo === 
 +
La funcionalidad de reemplazo se encapsula en un método estático en una clase ad hoc. Este método toma una excepción y la reemplaza por una RuntimeException copiando el mensaje, el stack trace y la causa.
 +
A su vez, antes de copiar la causa, la reemplaza llamándose a si mismo. De esta manera reemplaza toda la cadena en forma recursiva.
  
public class SafeHidratableException {
+
<code java>
 +
public class HidrataciónSegura {
  
public static RuntimeException secureAsRuntime(Throwable t) {
+
    public static RuntimeException asegurarComoRuntime(Throwable t) {
  
//Acepta parametro nulo y devuelve nulo en tal caso para soportar recursividad sobre las causas
+
        if (t == null) {
//sin necesidad de investigar si la causa en nula.
+
            return null;
if (t == null) {
+
        }
return null;
 
}
 
  
//Toma la causa de t y la asegura como RuntimeException llamando a este mismo metodo
+
        RuntimeException causaSegura = asegurarComoRuntime(t.getCause());
//en forma recursiva. La recursividad se corta cuando la causa es nula.
 
RuntimeException safeCause = secureAsRuntime(t.getCause());
 
  
//Toma el mensaje de T y le agrega cual fue la clase original de la excepcion que se esta asegurando la deserializacion.
+
        String mensaje =
String message = "[SafeHidratable for " + t.getClass().getName() + "] " + t.getMessage();
+
            "[Reemplazo de " + t.getClass().getName() + "] " + t.getMessage();
 
 
//Construye una runtime exception que tiene el nombre de la excepcion original, el mensaje original y la causa
+
        RuntimeException excepcionSegura =  
//asegurada.
+
            new RuntimeException(mensaje, causaSegura);
RuntimeException safeException = new RuntimeException(message, safeCause);
+
 +
        excepcionSegura.setStackTrace(t.getStackTrace());
  
//Copia el stackTrace
+
        return excepcionSegura;
safeException.setStackTrace(t.getStackTrace());
 
  
//devuelve T en forma de Runtime. A esta altura todas las causas ya fueron aseguradas tambien.
+
    }
return safeException;
 
 
 
}
 
 
 
 
}
 
}
 +
</code>

Revisión actual del 18:31 26 ago 2009

En Java, cuando se expone un método para ser invocado en forma remota, por ejemplo en un EJB, puede suceder que tire una excepción como respuesta a la invocación como consecuencia de algún error durante la ejecución. Esta excepción será serializada y pasada por red al método que realizó la invocación.

Cuando llega la excepción del otro lado tiene que volver a hidratarse para lo que es necesario contar con la definición de la clase a deserializar.

Con un poco de cuidado y haciendo las cosas bien, la clase de la Excepción existirá del lado del cliente. Pero también debemos tener en cuenta que las excepciones suelen tener anidada la causa que las produjo (otra excepción) y la causa puede tener anidada a su vez otra causa y así sucesivamente hasta llegar a la raíz del problema que originó el fallo.

Todas las clases de la cadena de causas deberá estar presente del lado del cliente que invoca el método remoto ya que intentará rehidratarlas. A veces, por alguna razón u otra, puede ser que no tengamos disponible las excepciones de alguna parte de la cadena de causas del lado del cliente. Deberíamos intentar solucionar este problema investigando porqué no están esas excepciones e incluirlas.

Como último recurso, si no fuera posible asegurar su existencia del lado del cliente, este ejemplo muestra como reemplazar las excepciones de la cadena por excepciones que estamos seguros que pueden ser hidratadas nuevamente.

Objetivo

El objetivo es eliminar el problema de la rehidratación de una excepción del lado del cliente con la mínima pérdida de información posible.

Una solución rápida sería cortar la cadena de causas, pero esto nos dejaría ciegos frente a un problema.

Otra solución sería arrojar una excepción que copie los stack trace de todas sus causas y organice material descriptivo, pero dejaría la información inaccesible con tratamientos estándar.

Finalmente se plantea reemplazar las excepciones de la cadena de causas con excepciones que estamos seguros que se pueden rehidratar del lado del cliente y se copia la información del mensaje y el stack trace de las causas originales.

Con esta solución, se sigue recibiendo excepciones encadenadas y se conserva el stack trace en cada una de ellas en forma estándar.

El costo que tiene es que se pierde la clase original de la excepción que se reemplace. Es por esto que debe utilizarse a conciencia.

Posible mejora

El ejemplo que se plantea hace un reemplazo masivo de las causas en el último momento posible. Una mejora importante sería discriminar que excepciones pueden no ser reemplazadas para conservar intacta la mayor parte posible de la cadena.

Escenario

Una clase Blah que tiene un métodoX que se invocará remotamente.

  • Se envuelve su funcionamiento en un bloque try para atajar las excepciones que se produzcan.
  • En el catch se reemplaza la excepción junto con su cadena de causas por excepciones que es seguro que se pueden rehidratar del lado del cliente.

public class Blah {

   public void metodoX() throws RemoteException {
       try {
          //Lo que hace el metodoX
       } catch (Exception e) {
          throw HidratacionSegura.asegurarComoRuntime(e);
       }
   }

}

Reemplazo recursivo

La funcionalidad de reemplazo se encapsula en un método estático en una clase ad hoc. Este método toma una excepción y la reemplaza por una RuntimeException copiando el mensaje, el stack trace y la causa. A su vez, antes de copiar la causa, la reemplaza llamándose a si mismo. De esta manera reemplaza toda la cadena en forma recursiva.

public class HidrataciónSegura {

   public static RuntimeException asegurarComoRuntime(Throwable t) {
       if (t == null) {
           return null;
       }
       RuntimeException causaSegura = asegurarComoRuntime(t.getCause());
       String mensaje = 
           "[Reemplazo de " + t.getClass().getName() + "] " + t.getMessage();
       RuntimeException excepcionSegura = 
           new RuntimeException(mensaje, causaSegura);
       excepcionSegura.setStackTrace(t.getStackTrace());
       return excepcionSegura;
   }

}