Mostrando entradas con la etiqueta exception. Mostrar todas las entradas
Mostrando entradas con la etiqueta exception. Mostrar todas las entradas

sábado, 10 de marzo de 2018

Control de Excepciones en Servicio REST de Spring

Ya vimos hace tiempo como hacer un servicio REST con Spring Boot aquí. Ahora veremos como hacer de una forma limpia el manejo de posibles excepciones. 

La teoría es sencilla, debemos crear otro método en la clase que contiene el servicio con la anotación '@ExceptionHandler'. De esta forma podremos indicar cual es la excepción que trataremos si es lanzada por nuestro servicio. Y tratarla como queramos, en este caso hemos creado un objeto JSON que muestra la URL del error y un mensaje explicativo del mismo. Además con '@ResponseStatus' podremos indicarle el código de error y una descripción. 

@ExceptionHandler(NotFoundException.class)
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Element not found")
public String handleNotFoundException(final HttpServletRequest req, final NotFoundException ex) {
 String errorURL = req.getRequestURL().toString();
 return new ErrorResponse(errorURL, ex.getMessage());
}

Otra opción si la excepción vamos a tener que tratarla en varios controladores, es crearla en una clase asociada a la anotación '@ControllerAdvice'. De esta forma será tratada para todos los controladores. Si debemos tener en cuenta que al no encontrarnos en un '@RestController' como en el caso anterior, deberemos indicar que la salida es '@ResponseBody' si queremos que tenga el mismo tratamiento que en el método anterior.

@ControllerAdvice
public class ExceptionController {
 @ExceptionHandler(ArithmeticException.class)
 @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
 @ResponseBody
 public String handleNotArithmeticException(final HttpServletRequest req, final ArithmeticException ex) {
  String errorURL = req.getRequestURL().toString();
 return new ErrorResponse(errorURL, ex.getMessage());
 }
}




viernes, 10 de febrero de 2017

Evitar Rollback en transacciones JPA

Una de las cosas buenas que nos ha traído JEE y el uso de JPA es el manejo de las transacciones. Como se gestionan ahora a través del software y como se puede persistir o revertir datos fácilmente. 

Pero se puede dar el caso que por motivos concretos queremos evitar que se realice un rollback de la operación si algo falla en la misma. El ejemplo típico puede ser porque dentro del propio service, envías un correo tras persistir los datos. Aún en el caso que hubiese un error con el servidor de correo, no querrías que se volviese atrás en la operación de BBDD. Para eso haríamos uso de 'noRollbackFor' o de 'dontRollbackOn' en función de la anotación @Transactional que estemos usando (Si es la propia de JEE o de Spring).


//org.springframework.transaction.annotation.Transactional
@Transactional( noRollbackFor = { MessagingException.class } )
public void createUser( final User user ) {
   //...   
}

//javax.transaction.Transactional
@Transactional( dontRollbackOn = { MessagingException.class } )
public void createUser( final User user ) {
   //...   
}

Pero debemos tener en cuenta que esta propiedad solo nos funciona con operaciones de tipo read-only. Por lo que llegamos al meollo de este post. Como podemos evitar que haga rollback en una operación que no es de tipo read-only, ya que en dicho caso no nos valdrán las propiedades anteriormente comentadas.

Para lograrlo haremos uso de la anotación de Spring y su propiedad 'propagation'. Esta propiedad es la que nos indicará cual es el comportamiento de las transacciones. Por defecto tiene el valor 'REQUIRED', y permite que los métodos continúen la transacción del método superior o creen una, en caso de que no exista.

Para solucionar nuestro problema deberemos indicarle a dicha propiedad el valor 'REQUIRES_NEW'. De esta forma, se creará un transacción específica para el método y en caso de error, evitará su propagación a métodos superiores. Quedando el código de la siguiente forma:

@Transactional( propagation = Propagation.REQUIRES_NEW, noRollbackFor = { MessagingException.class } )
public void createUser( final User user ) {
   dao.save(user);   
   emailService.sendCreationUserMail(user);   
}

miércoles, 12 de agosto de 2015

Not an managed type Exception

Vamos a volver un poco a los orígenes de hacer post relativos a errores que suelo encontrar cuando codifico. Este me ha ocurrido recientemente y tiene su pequeño truco. 

Este error viene dado por la configuración de JPA de una aplicación JEE con Spring, y es este:

Caused by: java.lang.IllegalArgumentException: Not an managed type: class

En principio si 'googleas' un poco todos te dirán que es porque la clase JPA de la cual quieres hacer persistencia no esta debidamente configurada. Para ello y según la documentación de Spring necesita una de estas 3 cosas:

  • Que tenga su identificador debidamente anotado. 
  • Que implemente Persistable
  • Que implemente EntityInformation


Y en un principio mi clase tenia sus identificador con la anotación correspondiente. Pero aún así al lanzar la aplicación me seguía dando el error. Además tenía un ejemplo muy parecido en otra aplicación y funcionaba correctamente. Probé con la opción 'packagesToScan' pero me seguía sin poder resolverlo hasta que vi el siguiente enlace. En el se indica una cosa clave


  • Las clases que se que contenga las anotaciones @Entity, @MappedSuperclass or @Embeddable y se encuentren en el mismo entorno que el fichero persistence.xml serán manejadas por JPA. 


Y esta es la solución, puesto que yo tenía un proyecto dividido en módulos diferenciados y donde se encontraba el fichero persistence.xml no erá el mismo donde se encontraban las clases JPA. Una vez que añadí el fichero persistence.xml al módulo de las clases JPA ya funciono correctamente. 

viernes, 22 de mayo de 2015

Manejo de excepciones en hilos

Recientemente he tenido que trabajar con hilos y un posible problema, es el lanzamiento de excepciones no controladas y por tanto la detención de la ejecución del hilo.

Para evitar estos problemas esta la interfaz UncaughtExceptionHandler, la cual nos permite implementarla e implementar su método uncaughtException, en el cual podemos realizar la lógica pertienente en caso de error no controlado. Se ve más claro en un ejemplo sencillo.

Por un lado tenemos el hilo, el cual puede que en determinadas ocasiones lance un tipo de excepción no controlada.

public class PruebaThread extends Thread {
 private int numHilo;
 public PruebaThread(int numero){
  super();
  numHilo = numero;
 }
 public void run() {
  Random rand = new Random();
  int randomNum = rand.nextInt(3);
  System.out.println("NumHilo: "+numHilo+" tiene randomNum: "+randomNum);
  if (randomNum == 0) {
   throw new NullPointerException();
  } else if (randomNum == 1) {
   throw new ArithmeticException();
  } else if (randomNum == 2) {
   throw new IllegalArgumentException();
  }
 }
}

Por otro lado implementamos la interfaz y controlamos estas posibles excepciones, para cada una de ellas es posible que queramos realizar un tratamiento distinto.

import java.lang.Thread.UncaughtExceptionHandler;

public class PruebaUncaughtExceptionHandler implements UncaughtExceptionHandler{
 @Override
 public void uncaughtException(Thread t, Throwable e) {
  if(e instanceof NullPointerException){
   System.out.println("Es NullPointerException");
  }else if(e instanceof ArithmeticException){
   System.out.println("Es ArithmeticException");
  }else if(e instanceof IllegalArgumentException){
   System.out.println("Es IllegalArgumentException");
  }
 }
}

Si ejecutamos el siguiente código podemos ver como es el funcionamiento de los hilos y su tratamiento de excepciones

public static void main(String[] args) {
  for (int i = 0; i < 8; i++) {
   PruebaThread thread = new PruebaThread(i);
   thread.setUncaughtExceptionHandler(new PruebaUncaughtExceptionHandler());
   thread.start();
  }
 }

Resultado:

NumHilo: 3 tiene randomNum: 1
Es ArithmeticException
NumHilo: 2 tiene randomNum: 1
Es ArithmeticException
NumHilo: 5 tiene randomNum: 1
Es ArithmeticException
NumHilo: 1 tiene randomNum: 2
Es IllegalArgumentException
NumHilo: 0 tiene randomNum: 2
Es IllegalArgumentException
NumHilo: 4 tiene randomNum: 0
Es NullPointerException
NumHilo: 6 tiene randomNum: 0
Es NullPointerException
NumHilo: 7 tiene randomNum: 1
Es ArithmeticException

lunes, 19 de mayo de 2014

Control de excepciones con Spring MVC

Hoy vamos a ver como podemos realizar el control de excepciones de una forma más limpia en los Controller de Spring MVC, sin tener que usar el famoso try/catch.

El control de excepciones podemos realizarlo de varias formas pero aquí indicaremos como realizarlo a nivel de clase y/o global. La forma de realizar el control será la misma lo que cambiará es el sitio donde se incluye para modificar su funcionamiento.

Veamos como hacer el control de la excepción. Creamos un método con la anotación ExceptionHandler y entre paréntesis la excepción que queremos capturar. Dentro del método podemos hacer diferentes tratamientos, en nuestro caso simplemente obtendremos el tipo de excepción y el usuario responsable. Estos datos podremos mostrarlos luego en la página de error.

@ExceptionHandler(ArithmeticException.class)
public ModelAndView handleAException(final ArithmeticException ex) {

 ModelAndView modelAndView = new ModelAndView("error.page");
 modelAndView.addObject("exception", ex.getClass().getSimpleName());
 String name = SecurityContextHolder.getContext().getAuthentication().getName();
 modelAndView.addObject("username", name);

 return modelAndView;
}


Mostramos un ejemplo de página de error y como hacemos uso de las variables pasadas:

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>


Error de ${exception} producido por ${user}


El ejemplo esta hecho con Tiles, por lo tanto error.page debe estar configurado para que nos redirija a dicha página.

Ahora viene decidir que tratamiento queremos dar a el manejador de la excepción. Si quiere ser local o global. En el caso de que queramos darle un tratamiento local, debido a que va a ser un tipo de excepción que solo puede darse en un determinado Controlador, debemos incluir el método dentro de dicho Controlador. En caso de querer darle un tratamiento global, debemos incluirla en una clase que cuenten con la anotación 'ControllerAdvice', ejemplo:

@ControllerAdvice
public class ExceptionController {
    protected final Logger logger = Logger.getLogger(getClass());

    @ExceptionHandler(IOException.class)
    public ModelAndView handleIOException(final IOException ex) {
  logger.info("handleIOException - Catching: " + ex.getClass().getSimpleName());
  ModelAndView modelAndView = new ModelAndView("error.page");
  return modelAndView;
    }
}


En el caso de que queráis saber algo más podéis verlo aqui, en la documentación oficial