2010-07-09 15 views
19

Me pregunto si alguien puede aconsejar: Tengo un escenario donde un trabajo programado que está siendo ejecutado por Quartz actualizará una lista de objetos cada hora.Cómo obtener y establecer un objeto global en el contexto del servlet de Java

Pero necesito esta lista de objetos para ser visible en todas las sesiones creadas por Tomcat. Entonces, lo que estoy pensando es que escribo este objeto en alguna parte cada hora desde el trabajo de Quartz que se ejecuta para que cada sesión pueda acceder a él.

¿Alguien puede decir cómo se puede lograr esto? Me preguntaba sobre el objeto que se está escribiendo en el contexto de servlet del trabajo de Quartz. La alternativa es hacer que cada sesión llene la matriz de objetos desde una tabla de base de datos.

Gracias

Mr Morgan.

+0

También hay una discusión útil de este desafío en esta otra publicación de SOF: http://stackoverflow.com/questions/10276041/retrieve-servletcontext-reference-in-quartz-scheduler – Steve

+0

Contexto de aplicación global como el de Spring ? – PSyLoCKe

Respuesta

12

Sí, almacenaría la lista en el ServletContext como un atributo con ámbito de aplicación. En su lugar, extraer los datos de una base de datos es probablemente menos eficiente, ya que solo está actualizando la lista cada hora. Crear un ServletContextListener puede ser necesario para dar a la tarea Quartz una referencia al objeto ServletContext. El ServletContext solo se puede recuperar de clases relacionadas con JavaEE como Servlets y Listeners.

EDITAR: En ServletContextListener, al crear el trabajo, puede pasar la lista al trabajo agregándolo a JobDataMap.

public class MyServletContextListener implements ServletContextListener{ 
    public void contextInitialized(ServletContextEvent event){ 
    ArrayList list = new ArrayList(); 

    //add to ServletContext 
    event.getServletContext().setAttribute("list", list); 

    JobDataMap map = new JobDataMap(); 
    map.put("list", list); 
    JobDetail job = new JobDetail(..., MyJob.class); 
    job.setJobDataMap(map); 
    //execute job 
    } 

    public void contextDestroyed(ServletContextEvent event){} 
} 

//Quartz job 
public class MyJob implements Job{ 
    public void execute(JobExecutionContext context){ 
    ArrayList list = (ArrayList)context.getMergedJobDataMap().get("list"); 
    //... 
    } 
} 
+0

Entonces, lo que estás diciendo es que tengo un oyente que tiene un método para poblar el objeto; este oyente se ejecuta cuando el contexto se inicializa y rellena el objeto. ¿El trabajo de Quartz puede llegar al objeto a través del oyente cuando se ejecuta cada hora? –

+1

Al mirar un comentario de otra respuesta, parece que ya tiene un ServletContextListener que inicia la tarea de Quartz? Lo que puede hacer es crear la lista en el oyente, luego agregar la lista como un atributo al ServletContext, luego pasar la misma lista a la tarea. A continuación, puede acceder a la lista en cualquier lugar de su aplicación web llamando a 'ServletContext # getAttribute()'. Por ejemplo, en un servlet, debe llamar a '(ArrayList) getServletContext(). GetAttribute (" list ")'. Asegúrese de sincronizar correctamente su lista para que no esté leyendo/escribiendo en ella al mismo tiempo. – Michael

+0

Esto suena bien, pero con una excepción. No estoy seguro si es posible 'pasar la misma lista a la tarea' dada la forma en que los trabajos son enviados por ScheduleController de Quartz. –

0

Bueno, si utiliza campos estáticos, serán visibles para todas las clases cargadas por el mismo cargador de clases. Creo que al menos los servlets de una aplicación deberían terminar calificando. Sin embargo, esto es ciertamente sucio.

Un objeto que se define y garantiza ser (más) global es el ServletContext. Esto se comparte entre todos los servlets que forman parte de una aplicación, es decir, cargados desde el mismo web.xml. Hay put y get llamadas para ServletContext que le permiten tratarlo esencialmente como un Mapa.

Más allá de eso, deberá encontrar las clases comunes a todas las aplicaciones web dentro de un servidor Tomcat. Tomcat hace mucho trabajo de pies con cargadores, y creo que diferentes aplicaciones web tendrán cargadores distintos. Puede evitar esto escribiendo su propia clase y colocando esa clase en los directorios common o shared de Tomcat. Si entiendo correctamente this description, esas clases estarán disponibles, UNA VEZ, para todas las aplicaciones web.

Finalmente, más allá de los confines de un solo servidor Tomcat, necesitará algún mecanismo basado en TCP/IP para comunicarse entre las JVM. Pero como entendí tu pregunta, eso no debería ser necesario.

+0

¿Pero puede acceder a ServletContext fuera de un servlet? El trabajo de Quartz que menciono es uno que implica una interfaz suya. Sin embargo, estoy dispuesto a usar una clase con estática. –

+0

Hmm. ¿Este trabajo de Quartz se ejecuta como un servlet o filtro dentro de Tomcat? Si es así, sí. Si se está disparando en una JVM externa, tendrá problemas para analizar el cerebro de Tomcat. Al mirar [este documento de Quartz] (http://www.quartz-scheduler.org/docs/cookbook/ServletInitScheduler.html), parece que Quartz estará dentro de su aplicación, y todo debería estar bien. –

+0

Al menos Quartz obtiene acceso a su ServletContext (en los dos escenarios descritos). Espero que Quartz tenga el buen sentido de hacer que ese contexto también sea accesible para sus usuarios API. –

1

Usted puede intentar alguna solución de almacenamiento en caché, como EhCache para almacenar los valores que, y actualizarlos cada hora. Manejará problemas de concurrencia. El objeto de caché se puede almacenar en el ServletContext

Una buena forma de escribir en el ServletContext del trabajo de Quartz es registrar oyentes en su trabajo que reciben una notificación sobre el valor modificado. Así, por ejemplo:

public class JobListener { 
    public void updateValue(Object newValue); 
} 

public class ServletContextCacheJobListener implements JobListener { 
    private ServletContext ctx; 
    public ServletContextJobListener(ServletContext ctx) { 
     this.ctx = ctx; 
    } 

    public void updateValue(Object newValue) { 
      Cache cache = (Cache) ctx.getAttribute("cache"); 
      cache.update("yourKey", newValue); 
    } 
} 

su trabajo tendrá un List<JobListener> y cuando se programa el trabajo, es una instancia del oyente concreto y añadirlo al trabajo.

+0

No estoy seguro de entender bien de dónde vienes. ¿Puedes aclarar? ¿Estás diciendo que mi trabajo de Quartz debería tener una lista de JobListener o ServletContextCacheJobListener? Si este último, presumiblemente, ¿establecería la lista un elemento a la vez? –

+0

Lea sobre el patrón Observer (oyente). Y 'ServletContextCacheJobListener' es un' JobListener' porque lo implementa. Entonces no hay diferencia – Bozho

Cuestiones relacionadas