2009-10-17 16 views
11

Sigo recibiendo esta "sugerencia" de muchos desarrolladores una y otra vez. En mi experiencia, he encontrado que las EJBExceptions son adecuadas para "fin del mundo" desde la perspectiva de la instancia de bean (como cuando algo está tan mal que la instancia de bean no puede recuperarse por sí misma). Si una instancia puede recuperarse, creo que es mejor lanzar una excepción de aplicación.¿Por qué arrojar una EJBException es una práctica "recomendada"?

Aquí es el patrón que me encuentro con una y otra vez:

 
private SomeResource resource; 
ejbCreate: 
    resource = allocateResource(...); 

omMessage: 
    try { 
    ... 
    } catch (JMSException e) { 
     throw new EJBException(e); 
    } 

ejbRemove: 
    freeResource(resource); 

mi humilde opinión este es un anti patrón que causa pérdidas de recursos.

EDITAR: En concreto, la especificación EJB dice que si un grano produce una excepción de tiempo de ejecución (y el error EJBException es una excepción de tiempo de ejecución) del método de negocio, a continuación, el grano se descarta sin llamar ejbRemove en él.

Es éste un ejemplo relevante que no lance un error EJBException? ¿Cuáles son los casos relevantes cuando se debe lanzar EJBException?

+0

@vinny_g: He agregado la explicación de su comentario sobre mi (ahora eliminada) "respuesta". –

Respuesta

7

El lanzamiento de EJBException es recomendado por la especificación EJB (14.2.2 en el EJB 3) en los casos en que el EJB no puede recuperarse de una excepción que encuentra. La especificación también dice que el EJB puede permitir razonablemente (sin marcar) excepciones del sistema para propagarse al contenedor.

Estoy de acuerdo con su lectura de la especificación de que en tales casos el contenedor no invocará los métodos de devolución de ciclo de vida, y por lo tanto su preocupación de que cualquier ordenamiento de recursos que normalmente suceda en la devolución de llamada ejbRemove() no ocurrirá y entonces hay un peligro de fuga de recursos.

Mi experiencia es que muchos problemas difíciles surgen debido a la falta de codificación defensiva. "La situación X no puede ocurrir en un sistema con buen comportamiento, y si lo hace, entonces todo el sistema está roto, por lo que no codificaré para esa eventualidad". Luego obtenemos un alineamiento "interesante" de las estrellas y los errores del operador y el "no puede suceder" sucede varias veces en rápida sucesión y los efectos colaterales imprevistos dan lugar a problemas realmente difíciles de diagnosticar.

Por lo tanto, yo diría:

  1. hacer todo lo posible para salir de la instancia del bean en un estado donde la siguiente invocación de un método de negocio podría ser capaz de trabajar. Esto podría significar capturar excepciones y cerrar recursos que están en error. En este caso, puede optar por decirle a las personas que llaman, "lo siento, no funcionó, pero si lo intentas más tarde ..." Lo hago lanzando una excepción marcada TransientException.
  2. Si no tiene un servicio de limpieza importante en ejbRemove, puede permitir que SystemExceptions se propague, pero no estoy seguro de que esto sea amigable. Dependes de una biblioteca y arroja un NullPointerException. ¿Es en realidad más robusto para atrapar y volver a lanzar TransientException?
  3. Si tiene un servicio de limpieza importante, entonces creo que debe detectar todas las excepciones y al menos intentar la limpieza. A continuación, puede optar por volver a lanzar EJBException o una excepción del sistema para que se destruya la instancia, pero al menos ha intentado hacer la limpieza.
0

Si los recursos de fugas dependen o no de cómo se administran esos recursos. El frijol descartado obtiene la basura recogida, renunciando a su referencia al recurso que obtiene basura recolectada cuando no hay más referencias que lo señalen. Si el recurso también se mantiene en una colección con una ruta de acceso a la raíz, se fugará a menos que el contenedor tenga un administrador de grupo que pueda detectar recursos inactivos y reciclarlo.

Estoy de acuerdo con usted en que el grano debe manejar cualquier excepción que puede por sí mismo y dejar que el resultado se conocerá a la persona que llama. Para mí, el patrón anti en la situación que describes utiliza excepciones para pasar el estado del objeto. Las excepciones deben ser excepciones, no usadas como valores de retorno.

2

El contenedor todavía puede confirmar la transacción actual aunque el método EJB haya lanzado una excepción de aplicación. Si su método de EJB puede lanzar una excepción de aplicación, entonces debería considerar el uso de EJBContext.setRollbackOnly() así:

public void method() throws ApplicationException { 
    try { 
    ... 
    } catch (ApplicationException e) { 
     _context.setRollbackOnly(); 
     throw e; 
    } 
} 

Lanzar un error EJBException o un sistema (sin marcar) excepción significa que el contenedor será automáticamente deshacer la transacción. Ver: http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/Transaction3.html

Si los EJB de su sistema no atrapan y manejan rutinariamente las excepciones de la aplicación, entonces el código que las arroja puede resultar en una operación parcialmente completa al comprometer sus cambios.

Esto puede llevar a errores difíciles de encontrar porque el "mito del usuario" de un método EJB con CMP es que se asigna a una transacción, que es atómica. La lógica comercial parcialmente completa está comprometida atómicamente con la base de datos. Esto es extremadamente confuso cuando sucede en la práctica. El artículo de Joel sobre abstracciones con fugas: http://www.joelonsoftware.com/articles/LeakyAbstractions.html explica por qué.

Si su sistema tiene EJB que no manejan las excepciones de la aplicación correctamente, la respuesta lógica es corregirlos para que lo hagan. Hasta que se solucione el problema, su equipo podría tener una razón racional para no querer que se emitan excepciones de aplicación desde los EJB.

Cuestiones relacionadas