minutes example every ejemplo bean java-ee ejb-3.1 schedule job-scheduling

java-ee - every - timer bean example



EJB @Schedule espere hasta que se complete el método (4)

Bueno, tuve un problema similar. Había un trabajo que se suponía que debía ejecutarse cada 30 minutos y, a veces, el trabajo tardaba más de 30 minutos en completarse; en este caso, comenzaba otra instancia de trabajo, mientras que la anterior aún no había terminado. Lo resolví teniendo una variable booleana estática que mi trabajo establecería en verdadero cada vez que comenzaba a ejecutarse y luego lo configuraba de nuevo en falso cuando terminaba. Como es una variable estática, todas las instancias verán la misma copia en todo momento. Incluso puede sincronizar el bloque cuando configura y desarma la variable estática. class myjob {private static boolean isRunning = false;

public executeJob(){ if (isRunning) return; isRunning=true; //execute job isRunning=false; } }

Quiero escribir un trabajo de fondo (EJB 3.1), que se ejecuta cada minuto. Para esto utilizo la siguiente anotación:

@Schedule(minute = "*/1", hour = "*")

que está funcionando bien

Sin embargo, a veces el trabajo puede tomar más de un minuto. En este caso, el temporizador todavía está activado, lo que causa problemas de subprocesamiento.

¿Es de alguna manera posible terminar el planificador si la ejecución actual no se completa?


Desde Java EE 7 es posible usar un ManagedScheduledExecutorService "enterado de EE", es decir, en WildFly:

En, por ejemplo, @Singleton @Startup @LocalBean , @Singleton @Startup @LocalBean el predeterminado "managed-scheduled-executor-service" configurado en standalone.xml :

@Resource private ManagedScheduledExecutorService scheduledExecutorService;

Programe alguna tarea en @PostConstruct para ser ejecutada, es decir, cada segundo con retraso fijo :

scheduledExecutorService.scheduleWithFixedDelay(this::someMethod, 1, 1, TimeUnit.SECONDS);

scheduleWithFixedDelay :

Crea y ejecuta una acción periódica que se habilita primero después de la demora inicial dada, y posteriormente con la demora dada entre la finalización de una ejecución y el comienzo de la siguiente. [...]

No apague el programador en ie @PreDestroy :

El servidor de aplicaciones administra las instancias del Servicio de ejecución programada administrada, por lo que las aplicaciones Java EE tienen prohibido invocar cualquier método relacionado con el ciclo de vida.


Me encontré con el mismo problema, pero lo resolvió de manera ligeramente diferente.

@Singleton public class DoStuffTask { @Resource private TimerService timerSvc; @Timeout public void doStuff(Timer t) { try { doActualStuff(t); } catch (Exception e) { LOG.warn("Error running task", e); } scheduleStuff(); } private void doActualStuff(Timer t) { LOG.info("Doing Stuff " + t.getInfo()); } @PostConstruct public void initialise() { scheduleStuff(); } private void scheduleStuff() { timerSvc.createSingleActionTimer(1000l, new TimerConfig()); } public void stop() { for(Timer timer : timerSvc.getTimers()) { timer.cancel(); } } }

Esto funciona configurando una tarea para ejecutar en el futuro (en este caso, en un segundo). Al final de la tarea, programa la tarea nuevamente.

EDITAR: se actualizó para refactorizar las "cosas" en otro método, de modo que podamos proteger las excepciones para que la reprogramación del temporizador siempre suceda


Si solo 1 temporizador puede estar activo al mismo tiempo, hay un par de soluciones.

En primer lugar, el @Timer probablemente debería estar presente en @Singleton . En un Singleton, los métodos están bloqueados por escritura de forma predeterminada, por lo que el contenedor se bloqueará automáticamente cuando intente invocar el método del temporizador mientras aún haya actividad en él.

El siguiente es básicamente suficiente:

@Singleton public class TimerBean { @Schedule(second= "*/5", minute = "*", hour = "*", persistent = false) public void atSchedule() throws InterruptedException { System.out.println("Called"); Thread.sleep(10000); } }

atSchedule está bloqueado por escritura de forma predeterminada y solo puede haber un hilo activo en él, incluidas las llamadas iniciadas por el contenedor.

Tras ser bloqueado, el contenedor puede volver a intentar el temporizador, por lo que para evitarlo usaría un bloqueo de lectura en su lugar y delegaría en un segundo bean (el segundo bean es necesario porque EJB 3.1 no permite actualizar un bloqueo de lectura a un bloqueo de escritura).

El contador de tiempo Bean:

@Singleton public class TimerBean { @EJB private WorkerBean workerBean; @Lock(READ) @Schedule(second = "*/5", minute = "*", hour = "*", persistent = false) public void atSchedule() { try { workerBean.doTimerWork(); } catch (Exception e) { System.out.println("Timer still busy"); } } }

El frijol trabajador:

@Singleton public class WorkerBean { @AccessTimeout(0) public void doTimerWork() throws InterruptedException { System.out.println("Timer work started"); Thread.sleep(12000); System.out.println("Timer work done"); } }

Es probable que esto siga imprimiendo una excepción ruidosa en el registro, por lo que una solución más detallada pero más silenciosa es usar un booleano explícito:

El contador de tiempo Bean:

@Singleton public class TimerBean { @EJB private WorkerBean workerBean; @Lock(READ) @Schedule(second = "*/5", minute = "*", hour = "*", persistent = false) public void atSchedule() { workerBean.doTimerWork(); } }

El frijol trabajador:

@Singleton public class WorkerBean { private AtomicBoolean busy = new AtomicBoolean(false); @Lock(READ) public void doTimerWork() throws InterruptedException { if (!busy.compareAndSet(false, true)) { return; } try { System.out.println("Timer work started"); Thread.sleep(12000); System.out.println("Timer work done"); } finally { busy.set(false); } } }

Hay algunas variaciones más posibles, por ejemplo, puede delegar la verificación de ocupado a un interceptor, o inyectar un singleton que solo contiene el booleano en el bean timer, y verificar que boolean allí, etc.