Structuring Exception Handling For Dependable Component-Based Software Systems Estructura de manejo de exepciones para un software Component-Based confiable Autores: - Paulo Asterio de C. Guerra - Fernando Castor Filho - Vinicius Asta Pagano - Cecília Mary F. Rubira 1 Introducción El impulso para desarrollar este artículo fueron las dificultades con la que los programadores de sistemas de software hechos en base a componentes se encontraban, una y otra vez, al enfrentar grandes sistemas en donde se debían integrar partes de software. Estas eran hechas por programadores que al momento de escribirlas no sabían donde iban a ser implementadas, y por otra parte quienes debían unirlos no sabían como estaban conformados los componentes. La separación de tareas genera complicaciones extra a la hora de implementar el manejo de excepciones. Y además, no había un método general para el manejo de excepciones en sistemas de este tipo. Al momento de realizar la implementación, es importante definir una manera de cómo y por quien van a ser tratado el manejo de excepciones entre los componentes y conectores, si esto no se resuelve puede derivar en dos situaciones indeseables: el contexto y la semántica de la excepción se pierden, haciendo imposible que otros componentes lo manejen; o la excepción será ignorada y propagara errores a lo largo del sistema. Para estudiar la implementación de manejo de excepciones partieron de un modelo de componente, que ya había sido propuesto por T. Anderson y P. A. Lee, llamado IFTC (idealizad fault-tolerance component), este modelo es una clase, objeto, o incluso un sistema, que separa la actividad normal de la actividad anormal. Al recibir la petición de un servicio, se pueden dar tres casos: una respuesta normal, que implica el pedido haya sido procesado satisfactoriamente; una excepción de interfaz indicando que fue un pedido no valido; o un excepción de tipo error, es decir que fue un pedido valido pero no pudo ser procesado. En base al modelo visto, se supone que un componente debería poder detectar cuando sucedan condiciones anormales ya previstas. Pero a la hora de realizar el desarrollo de la arquitectura el implementador deberá tener en cuenta aquellas situaciones que el componente podría no contemplar, para obtener un sistema mas robusto, por lo tanto estos casos quedan dependiendo de la forma en que los componentes sean organizados, lo que podría causar problemas en la arquitectura a la hora realizar la integración del sistema. 1 Para evitar los efectos que esta forma de trabajo puede causar, se propone una estrategia dividida en dos partes complementarias. Un manejo de excepciones entre componentes y otro a nivel interno de los componentes. Ambos comparten una jerarquía de excepciones para expresar la semántica de fallas de un componente o un conector. Estrategia propuesta Luego de analizar, diseñar el sistema y los modelos de fallos de componentes y arquitectura, se propone a tener en cuenta los siguientes pasos descriptos en la publicación: 1. En caso de ser necesario, implementar la jerarquía de excepciones descripta a continuación, L a s u p e r c l a s e de la jerarquía (tipo abstracta) de excepciones es Excepción, que se divide en ExcepcionDeclarada y ExcepcionNoDeclarada, esta ultima a su vez se divide en ExcepcionPedidoRechazado(el pedido no fue procesado por no ser no ser correcto, es decir no cumplio el contrato, pero el sistema aun es consistente) y ExcepcionFallo (el pedido no fue procesado porque el sistema fallo). Esta ultima clase se subdivide en ExcepcionFalloRecuperado (el componente quedo estable a pesar del fallo) y ExcepcionFalloNoRecuperado(el fallo dejo al componente en estado no consistente). 2. Implementar el manejo de excepciones a nivel de componente (ALE handlers), que se ocupan principalmente de excepciones que son previstas a nivel interno de componentes y declaradas en las interfaces requeridas. La implementación de clases se encarga de: detectar las condiciones anormales previamente anticipadas y lanza una excepción declarada; hallar otras condiciones excepcionales especificas de la implementación del componente y lanza una excepción interna; ejecuta una “limpieza” al finalizar un bloque try-catch, en java existe el bloque finally. La fachada es una interfaz entre el cliente que hace el pedido y otra interfaz. Se utiliza como intermediario para facilitar el acceso al servicio requerido. 2 Los ALE se encargan de manejar: - excepciones lanzadas por un error en la interfaz - excepciones producidas por la implementación interna del componente - excepciones causadas por otros componentes En el caso que sea posible, se corrige el error mas adelante. También se ocupa de las excepciones producidas entre la fachada y la interfaz. Las excepciones declaradas en el componente y las que sean ExcepcionPedidoRechazado se propagan directamente a quien haya solicitado el servicio. Para otras excepciones se puede retroceder en el proceso y si resulta exitoso se devuelve ExcepcionFalloRecuperado, o en caso contrario se lanza ExcepcionFalloNoRecuperado. En el caso de que los ALEH no sean aplicables, ya que no se dispone del código fuente para modificar un componente, una poderosa herramienta son las envolturas que permiten transformarlos como son vistos externamente para el manejo de excepciones. 3. Implementar el manejo de excepciones a nivel de arquitectura (CLE handlers), que se encargan de lidiar con todas las excepciones no declaradas, lanzadas por componentes preexistentes a la hora de implementarlos en un nuevo sistema. Debido a que los desarrolladores no saben donde sus componentes van a ser usados, resulta que los conectores son el lugar mas indicado para situar a los CLE handlers. Estos se ocupan de la recuperación de errores y enmascararlos a nivel de arquitectura y de resolver las fallos por problemas semánticos (darle nombres o significados distintos al mismo problema) entre cliente y servidor. En el caso de recibir una falla se debe encargar de solicitar el mismo pedido original a otro servidor, en caso de que lo haya. Si un servidor falla frecuentemente, el CLE debe ser capaz de detectarlo y aislarlo, y buscar uno alternativo que pueda darle el servicio buscado. Si no hay servidor alternativo devuelve al cliente la falla, que puede propagarse directamente o en caso de no poder, debe envolverla para que pueda ser recibida y entendida por el cliente o en su defecto una ExcepcionNoDeclarada es lanzada. 3 A modo de guía los autores proponen las siguientes directivas de cómo deben tratarse las excepciones en el caso de que un servidor levante la excepción E1 luego de que el cliente le envía una petición. - Si E1 se declara en el interfaz requerido del componente del cliente y el interfaz proporcionado del componente del servidor, E 1 debe ser propagada automáticamente. - Si E1 se declara en el interfaz proporcionado del componente del servidor y hay un tipo de excepción correspondiente E2 (semántica compatible) declarado en el interfaz requerido del componente del cliente, una excepción del tipo E2 se propaga. Un ejemplo típico de este panorama ocurre cuando las excepciones E1 y E2 tienen un antepasado común, E3; Otro ejemplo ocurre cuando E2 es un supertype de E1 - Si E1 se declara en el interfaz proporcionado del componente del servidor y no se puede traducir según las dos reglas indicadas arriba, después tres diversos resultados son posibles: -- Si E1 fue señalado porque la petición recibida por el componente del servidor era inválida, y el estado de este último no fue cambiado, una excepción del tipo RejectedRequestException debe ser señalada; -- Si E1 fue señalado porque el componente del servidor recibió una petición válida y no podía procesarla, si el estado del componente no se ha dañado, un subtipo de RecoveredFailureException se señala; -- Si E1 fue señalado porque el estado del componente del servidor se ha dañado de alguna manera, cualquiera debido a una petición inválida de debido a una petición válida que no podría ser procesada, un subtipo de UnrecoveredFailureException debe ser señalada. - Si E1 es un subtipo de UndeclaredException, puede ser propagado automáticamente; - Si E1 no se declara en el interfaz proporcionado del componente del servidor y no es un subtipo de UndeclaredException, la excepción propagada debe estar de un caso de UnrecoveredFailureException, indicando que el componente del servidor puede estar en un estado contrario. Resultados 4 Esta metodología fue puesta a prueba, para el caso de una empresa brasilera, dedicada a brindarle información al viajante en el subsistema de quejas, y se obtuvieron los siguientes resultados: Al estar separados los comportamientos normales y anormales, se pueden modificar el manejo de excepciones sin afectar el funcionamiento general del sistema y lo que resulta mejor aún es que al estar en clases separadas pueden ser reutilizados y esto resulta beneficioso ya que la cantidad de lugares en que las excepciones pueden ocurrir es en general mucho mayor que las maneras de tratarlas. Adaptar un sistema a esta estrategia para el manejo de errores llevo movilizar cerca del 5% de un total de 9000 líneas de código, lo cual es aceptable. Gracias a que la implementación de conectores fue hecha desde un principio, se utilizaron solamente seis conectores. En cambio, adaptar la jerarquía de excepciones demando mucho mas trabajo, revisar mas de 250 lugares en donde excepciones eran lanzadas. Son de gran importancia las opiniones y propuestas de quienes implementan el sistema para futuras correcciones. Conclusiones No resulta tarea sencilla la implementación de manejo de excepciones en sistemas basados en componentes, de tal manera que resulten robustos y reutilizables. Por lo que es bastante útil tener una guía o estrategia de pasos a seguir, que no complica por demás el desarrollo, ya que los modelos de componentes mas usados no lo proveen. Uno de los aspectos a resaltar del trabajo es la división de manejo de excepciones, entre el nivel de componentes y de conectores propuesto para poder ser reutilizado en distintas aplicaciones. A pesar de la utilidad de esta labor, aún tiene algunas limitaciones en las que se deberá trabajar en el futuro. Por ejemplo, no es aplicable a lenguajes que no soporten manejo de excepciones. Otro aspecto en el que deberá desarrollarse será la creación de una herramienta que permita una implementación más automática que maneje las excepciones en ambos niveles, así como también la aplicación de la división de comportamiento normal y anormal basada en la programación orientada a aspectos. 5