Lucas,
Boa! Essa TaskScope seria massa.
Eu acabei fazendo uma coisa bem parecida, só que ao invés de disparar uma Task a parte para fechar a sessão, fiz aquele esquema de uma superclasse abstrata que já implementa o run, abre a session e a transaction, executa um método abstrato e fecha a session depois.
Daí cada Task apenas implementa o método abstrato, tipo um Template method.
public abstract class Task implements Runnable {
@Override
public void run() {
try {
DaoManagerUtils.begin();
setup();
executar();
DaoManagerUtils.commit();
} finally {
DaoManagerUtils.rollback();
}
}
/**
* Deve configurar daos, services, etc
*/
public abstract void setup();
/**
* Lógica a ser agendada
*/
public abstract void executar();
}
(Eu estou ligado que esse rollback no finally está feio, mas no código da DaoManagerUtils abaixo fica claro o motivo).
Esse método setup está aí pois se eu passar a sessão / daos prontas no construtor, corro o risco de timeout de sessão até a tarefa começar a executar.
Também estou aproveitando esse mecanismo que foi construído com ThreadLocal abaixo (cada Thread fica com sua própria sessão).
Doravan.
É, nós temos um código de agendamento bem parecido aqui (seguindo o cookbook do VRaptor).
Tivemos só dois problemas com essa técnica.
O primeiro foi controle de transações como discutido acima.
O pessoal que estava no projeto fez um mecanismo de controle de Sessão individual para tasks usando variáveis static e a classe ThreadLocal (http://download.oracle.com/javase/6/docs/api/java/lang/ThreadLocal.html). Funciona bem. O único problema é o risco de vazar sessão se alguém esquecer de dar close() (por isso o Template Method acima).
public class DaoManagerUtils {
/** The Constant threadSession. */
private static final ThreadLocal<Session> threadSession = new ThreadLocal<Session>();
/**
* Obtem o(a) session.
*
* @return o(a) session
*/
public static Session getSession() {
SessionFactoryCreator factoryCreator = SharedContext.getContainer()
.instanceFor(SessionFactoryCreator.class);
try {
if (threadSession.get() == null) {
return factoryCreator.getInstance().getCurrentSession();
}
} catch (Exception e) {
if (threadSession.get() == null) {
threadSession.set(newSession());
}
}
return threadSession.get();
}
/**
* New session.
*
* @return the session
*/
public static Session newSession() {
SessionFactoryCreator factoryCreator = SharedContext.getContainer()
.instanceFor(SessionFactoryCreator.class);
return factoryCreator.getInstance().openSession();
}
/**
* Inicia a transação.
*/
public static void begin() {
if (getTransaction() != null && !getTransaction().isActive()) {
getSession().getTransaction().begin();
}
}
/**
* Reverte as operações realizadas.
*/
public static void rollback() {
if (getTransaction() != null && getTransaction().isActive()) {
getSession().getTransaction().rollback();
if (threadSession.get() != null) {
getSession().clear();
}
}
threadSession.set(null);
}
/**
* Finaliza a operação.
*/
public static void commit() {
getSession().getTransaction().commit();
if (threadSession.get() != null) {
getSession().clear();
}
threadSession.set(null);
}
/**
* Obtem a transação.
*
* @return Transaction
*/
public static Transaction getTransaction() {
return getSession().getTransaction();
}
}
A segunda coisa é para excluir uma Task agendada.
Hoje está sendo feita uma coisa assim:
ThreadPoolTaskScheduler tps = (ThreadPoolTaskScheduler) scheduler;
ScheduledThreadPoolExecutor stp = (ScheduledThreadPoolExecutor) tps.getScheduledExecutor();
stp.getQueue().clear();
Daí todas as tasks são lidas do banco novamente. (Está ficando um pouco complicado para dar manutenção nisso devido a quantidade de tipos de tarefas que está crescendo, cada uma precisando de parâmetros diferentes para rodar corretamente).
O método remove() não funciona pois não tenho como guardar a Runnable original. Da mesma forma, não tenho como quardar o objeto ScheduledFuture para chamar cancel(). Eu tenho que dar um jeito de percorrer os Runnables agendados e matar por ID, ou algo assim.
Se alguém tiver uma receita para cancelar um task que foi agendada via scheduler.schedule() sem ter o objeto ScheduledFuture seria ótimo.
Se não, caso alguém saiba fazer isso com Quartz por exemplo, seria ótimo também.