Diferencia entre revisiones de «Excepciones Seguras De Deserializar»

De Dos Ideas.
Saltar a: navegación, buscar
Línea 5: Línea 5:
 
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 deberá estar presente del lado del cliente que invoca el método remoto ya que intentará rehidratarlas.
+
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 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.
  
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.
+
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.
  
La idea es poder contar del lado del cliente con la mayor cantidad de información posbile de la forma más comunmente esperada.
+
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, como se esperaría en cualquier caso, y se conserva el stack trace en cada una de ellas como se esperaría en cualquier caso.
+
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.
 
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.
  
  
=== En el método que se invocará remotamente ===
+
=== 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.
 +
 
 +
 
 +
=== Escenrio ===
 +
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 Xxx {
+
<code java>
 +
public class Blah {
  
     public void metodoRemoto() throws RemoteException {
+
     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 35: Línea 45:
  
 
}
 
}
 +
</code>
 +
  
 
+
=== Reemplazo recursivo === 
 +
La funcionalidad de reemplazo se encapsula en un método estático en una clase adHoc. 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, reemplazando toda la cadena en forma recursiva.
  
public class SafeHidratableException {
+
<code java>
 +
public class HidrataciónSegura {
  
 
public static RuntimeException secureAsRuntime(Throwable t) {
 
public static RuntimeException secureAsRuntime(Throwable t) {
 
//Acepta parametro nulo y devuelve nulo en tal caso para soportar recursividad sobre las causas
 
//sin necesidad de investigar si la causa en nula.
 
 
if (t == null) {
 
if (t == null) {
 
return null;
 
return null;
 
}
 
}
 
//Toma la causa de t y la asegura como RuntimeException llamando a este mismo metodo
 
//en forma recursiva. La recursividad se corta cuando la causa es nula.
 
 
RuntimeException safeCause = secureAsRuntime(t.getCause());
 
RuntimeException safeCause = secureAsRuntime(t.getCause());
 
+
String message = "[Reemplazo de " + t.getClass().getName() + "] " + t.getMessage();
//Toma el mensaje de T y le agrega cual fue la clase original de la excepcion que se esta asegurando la deserializacion.
 
String message = "[SafeHidratable for " + t.getClass().getName() + "] " + t.getMessage();
 
 
//Construye una runtime exception que tiene el nombre de la excepcion original, el mensaje original y la causa
 
//asegurada.
 
 
RuntimeException safeException = new RuntimeException(message, safeCause);
 
RuntimeException safeException = new RuntimeException(message, safeCause);
 
//Copia el stackTrace
 
 
safeException.setStackTrace(t.getStackTrace());
 
safeException.setStackTrace(t.getStackTrace());
 
//devuelve T en forma de Runtime. A esta altura todas las causas ya fueron aseguradas tambien.
 
 
return safeException;
 
return safeException;
 
 
}
 
}
 
 
 
}
 
}
 +
</code>

Revisión del 12:06 7 abr 2009

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.


Escenrio

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 adHoc. 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, reemplazando toda la cadena en forma recursiva.

public class HidrataciónSegura {

public static RuntimeException secureAsRuntime(Throwable t) { if (t == null) { return null; } RuntimeException safeCause = secureAsRuntime(t.getCause()); String message = "[Reemplazo de " + t.getClass().getName() + "] " + t.getMessage(); RuntimeException safeException = new RuntimeException(message, safeCause); safeException.setStackTrace(t.getStackTrace()); return safeException; }

}