En el articulo anterior hemos usado las anotaciones de Servlets 3.0 para dar de alta un servlet sin tener la necesidad de hacer uso del web.xml.En este articulo introduciremos el concepto de servlet asincrono . Supongamos que disponemos del siguiente servlet.
package com.arquitecturajava; //omitimos imports /** * Servlet implementation class HolaMundo */ @WebServlet("/HolaMundo") public class HolaMundo extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter pw= response.getWriter(); pw.println("<html>"); pw.println("<body>"); for (int i = 0; i < 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } pw.println("hola mundo"); pw.println("</body>"); pw.println("</html>"); } }
Como se puede ver el servlet tiene un buble for que le obliga a dormir durante un total de 10 s simulando una tarea que consume recursos y lleva bastante tiempo ejecutar.
Al tardar 10 segundos en ejecutar la tarea el usuario no recibirá información alguna sobre que es lo que ha sucedido y le dará la sensación que la aplicación ha fallado.Para evitar esta situación podemos construir un servlet asincrono .Este nuevo tipo de servlet se declara de la siguiente manera.
package com.arquitecturajava; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.AsyncContext; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(name = "HolaMundo", urlPatterns = { "/HolaMundo" }, asyncSupported = true) public class HolaMundo extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { final PrintWriter pw = response.getWriter(); pw.println("<html>"); pw.println("<body>"); pw.println("hola mundo asincrono tarea realizandose en background"); System.out.println("Hilo Principal:" + Thread.currentThread().getName()); final AsyncContext contextoAsincrono = request.startAsync(); contextoAsincrono.setTimeout(12000); contextoAsincrono.start(new Runnable() { @Override public void run() { for (int i = 0; i <= 10; i++) { System.out.println("Hilo Tarea Asincrona :" + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { } } contextoAsincrono.complete(); } }); pw.println("</body>"); pw.println("</html>"); pw.close(); } }
Es muy similar a un servlet clásico con la peculiaridad de que soporta un nuevo atributo “asyncSupported = true”. Vamos a comentar mas a detalle el siguiente bloque de código del método doGet.
pw.println("hola mundo asincrono tarea realizandose en background"); System.out.println("Hilo Principal:" + Thread.currentThread().getName()); final AsyncContext contextoAsincrono = request.startAsync(); contextoAsincrono.setTimeout(12000); contextoAsincrono.start(new Runnable() { @Override public void run() { for (int i = 0; i <= 10; i++) { System.out.println("Hilo Tarea Asincrona :" + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { } } contextoAsincrono.complete(); } });
El método en cuestión modifica la forma de ejecutar el servlet a traves de la siguiente linea de código
final AsyncContext contextoAsincrono = request.startAsync();
Esta linea define un contexto de ejecución asincrono para que mas adelante podemos pasar a este contexto un objeto de tipo Runnable (para lanzar un thread en paralelo)
contextoAsincrono.start(new Runnable() {
Una vez hechas estas dos operaciones ,el contexto lanzara un nuevo thread para ejecutar la tarea que nosotros le hemos solicitado de forma asincrona. Sin penalizar al thread actual que esta ejecutando el método doGet del servlet
A continuación podemos ver en la consolo de Tomcat como el Servlet se ejecuta en el hilo principal (hilo 5)y la tarea asincrona en otro hilo (hilo 6).
Hola, acabo de encontrar tu web y me parece muy buena por la información que brindas. Quiero aprovechar a consultarte algo. Quiere ejecutar tareas de actualización de mis entidades (uso Hibernate) justo cuando mi aplicación se despligue, por eso uso el contextInitialized del una clase que implementa a ServletContextListener, el problema surge que cuando intento hacer uso del objeto que llama a al método de actualización me sale error en ejecución de “Puntero nulo” y asumo que es porque hasta ese momento no se han los beans(configurado en un fichero xml) y sus dependencias. ¿Cómo puedo hacer? Te dejo el… Read more »
Tienes que cargar antes el contexto de spring framework, antes de poderlo usar , apuntate al curso gratuito de introducción a Spring boot que viene un ejemplo de esos temas sino recuerdo mal 🙂
El ejemplo que pones es muy claro pero me gustaría hacer ampliaciones; por ejemplo ¿Cómo se haría para que una variable puesta en el for de pausa metido en el método run() (que imita la tarea pesada) imprima la variable de ese contador, pero no en el bucle, sino en la salida (response), justgo después de la llamada al método complete(). Por de pronto, la variable del for, no podría ser local al for y tampoco conviene variable miembro (por compartirse entre todos los servlets) y si la hago variable local (por encima del for) no compila por decirme que… Read more »
Hola Cecilio, he copiado el ejemplo, pero no consigo hacerlo funcionar, alguna pista?
jul 01, 2013 5:21:15 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() para servlet HolaMundo2Asincrono lanzó excepción
java.lang.IllegalStateException: Not supported.
at org.apache.catalina.connector.Request.startAsync(Request.java:1673)
at org.apache.catalina.connector.Request.startAsync(Request.java:1666)
at org.apache.catalina.connector.RequestFacade.startAsync(RequestFacade.java:1023)
at javax.servlet.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:364)
at es.dbapps.HolaMundo2Asincrono.doGet(HolaMundo2Asincrono.java:32)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.netbeans.modules.web.monitor.server.MonitorFilter.doFilter(MonitorFilter.java:393)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:724)
Uso Nebeans 7.3.1 y el tomcat que viene incorporado Tomcat/7.0.34
En eclipse si funcionó. Algo le pasa al Netbeans, pero vamos está sin tunear ni nada, tal cual lo instale.
Simplemente tu servidor no soporta la especificación . Comprueba que tu servidor soporta Servlet 3.0
Creo que ya tengo claro el tema.
Lo que se consigue con los servlets asíncronos es que las peticiones (tareas a realizar) se procesen en un entorno asíncrono , en threads distinos del thread principal. Con esto conseguimos que el thread principal no se bloquee y retorne al contenedor a encargarse de otras tareas.
¿es asi?
Entiendo que estas tareas a realizar en el entorno asíncrono tanto pueden devolver un response al navegador para visivilizar datos como realizar otro tipo de funciones.
¿?
En estos momentos estoy estudiando las especifiaciones de Servlets 3.0, estoy mirando el tema de los servlets asincronos. Lo que muestras es muy aclaratorio pero tengo dudas como buen principiante.
Entiendo que el código que desarrollas en el servlet tiene como función clarificar el tema, pero, en un entorno real, aquí mis dudas, ¿debemos decidir a través del código que peticiones entran en el entorno asíncrono y cuales no(respuesta instantánea)? ¿o todas las peticiones se canalizan a través del entorno asíncrono?
Espero haberme explicado bien.
Normalente las tareas sincronas las realizarás con un servlet normal (sincrono) . Mientras que algunas tareas especiales (de larga duración) las delegaras en un servlet asincrono.Espero te aclaré la pregunta.