[vRaptor-tasks] Como executar uma task?

Bom dia pessoal, tudo bem?

Eu estava tentando fazer uma task manualmente que acionava uma Thread para que a página não ficasse parada.
O problema é que essa task usa um file que o usuário faz upload e depois ele faz uma série de operações.
Uma delas é no banco de dados. E eu percebi que o vRaptor fecha a Session do Hibernate depois que ele finaliza a requisição.

Por ser em uma thread, o vRaptor finaliza a session antes que eu possa usá-la.

Eu dei uma pesquisada e vi o plugin do wpivotto: https://github.com/wpivotto/vraptor-tasks

Eu baixei o jar e estou tentando utilizá-lo. O problema é que eu me perdi um pouco na explicação dele.

Estou com algumas dúvidas:

  1. Essa task vai ser executada em paralelo? (Como se fosse em um Thread)
  2. Eu posso receber as minhas dependências no construtor da task? (Igual um @Component)
  3. Como que eu faria para passar o arquivo que foi feito o upload?

Essa aqui era a minha classe antigamente:

public class BudgetTrustTask implements Runnable {

	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	private BudgetTrustService service;
	private InputStream csvFile;

	public BudgetTrustTask(BudgetTrustService service, InputStream csvFile) { // Basta fazer um new BudgetTrustTask que ele roda o service.
		this.service = service; // O service tem como dependência um DAO, que tem uma Session como dependência, e outro Service...
		this.csvFile = csvFile;

		new Thread(this).start();
	}

	@Override
	public void run() {
		try {
			this.service.populateBudgetTrust(this.csvFile);
		} catch (Exception e) {
			this.logger.error("Unable to populate budget trust report.", e);
		}
	}
}

Aqui está como eu inicio essa task:

@Post("/budget/trust/load/")
	public void load(UploadedFile file) {
		try {
			if (file == null)
				throw new IllegalArgumentException(
						"UploadedFile 'file' cannot be null.");

			new BudgetTrustTask(this.budgetTrustService, file.getFile()); // Inicia a task e finaliza a requisição.

			this.service.success("reportWillBeProcessed");
		} catch (Exception e) {
			this.logger.error("Unable to populate budget trust report.", e);
			this.service.error("reportWontBeProcessed");
		}

		this.result.redirectTo(this).list();
	}

talvez seja isso que vc procura:

em todo caso, se não funcionar, vc pode criar um componente que executa esse carregamento assincrono…

nunca controle Threads na mão, pq elas são caras pra criar no sistema operacional, use um ThreadPoolExecutor ou ExecutorService (fixo, num @ApplicationScoped)… e receba uma SessionFactory nesse componente e instancie os daos na mão, não esquecendo de fechar a session ao final da task.

outro jeito de resolver isso é fazer uma task que dispara uma nova requisição ao servidor, daí a task consegue usar a session da request. Só não daria pra passar o arquivo dessa maneira.

Certo, e esse componente application scoped teria um método que desencadearia todo o processo e, nesse método, eu passo o arquivo…

Eu tinha pensado em algo nesse sentido, mas não me atentei ao fato de ter que instanciar todas as dependências e ao controle manual de Thread…

Eu vou trabalhar nesse componente, depois retorno com alguma resposta ou solução.

pra não ter que criar tudo na mão eu lembro de já ter criado um AtomicSession, que faz todas as operações comuns de forma “atomica” (abre a session e tx, faz a operaçao, commita e fecha)

daí receber esse cara no componente que agenda tudo.

Eu não consegui encontrar essa classe. Está em qual versão do VRaptor? Estou usando a 3.3.1 nesse projeto.

Bom Rafael, sobre suas dúvidas:

  1. Essa task vai ser executada em paralelo? (Como se fosse em um Thread)

A execução da task é encapsulada dentro de um job do quartz, então sim.
Se você desejar que somente uma task execute por vez (uma task demorada) use a opção concurrent = false

  1. Eu posso receber as minhas dependências no construtor da task? (Igual um @Component)

Sim, mas somente @ApplicationScoped e @PrototypeScoped. Se você precisar @RequestScoped basta anotar um método de um controller qualquer com @Scheduled
e o plugin irá invocar o método (chamando a url via get) no período especificado. Assim você pode trabalhar com as dependências normalmente.

  1. Como que eu faria para passar o arquivo que foi feito o upload?
    Estou trabalhando nisso, na próxima versão você passará uma mapa com os parâmetros desejados e o plugin injetará eles na task, ou setando os fields ou no método setup…

Enquanto essa versão não sai você pode fazer o seguinte


@Resource
public class BudgetTrustController {  

private final BudgetTrustService service;  

private final Stack<File> fileStack = new Stack<File>();

BudgetTrustController(BudgetTrustService service) {
	this.service = service;
}

@Post("...")
public add(File file) {
	stack.push(file);
}

//lógica da task (somente pode ser invocado de dentro do servidor, um interceptor garante isso para você
@Get("...")
@Scheduled(fixedRate = 10000)
public populate() {
	File file = stack.pop(); 
	service.populateBudgetTrust(file);
}

Sobre esse job do quartz, quando eu tentei executar a Task, houve um erro de ClassNotFound, aonde eu encontro o jar que contém esse job?

Existe alguma forma de baixar os jars do vraptor-tasks e suas dependências sem ser pelo maven?

[quote=Rafael Guerreiro]Sobre esse job do quartz, quando eu tentei executar a Task, houve um erro de ClassNotFound, aonde eu encontro o jar que contém esse job?

Existe alguma forma de baixar os jars do vraptor-tasks e suas dependências sem ser pelo maven?[/quote]

https://github.com/wpivotto/vraptor-tasks/downloads

vai precisar do quartz 2.1.1 tmb e suas dependências

o AtomicSession foi uma classe que eu criei :stuck_out_tongue: nao eh do vraptor

[quote=wpivotto]
Enquanto essa versão não sai você pode fazer o seguinte

[code]

@Resource
public class BudgetTrustController {

private final BudgetTrustService service;

private final Stack fileStack = new Stack();

BudgetTrustController(BudgetTrustService service) {
this.service = service;
}

@Post("…")
public add(File file) {
stack.push(file);
}

//lógica da task (somente pode ser invocado de dentro do servidor, um interceptor garante isso para você
@Get("…")
@Scheduled(fixedRate = 10000)
public populate() {
File file = stack.pop();
service.populateBudgetTrust(file);
}

[/code][/quote]
Certo, mas e quanto à esse fixedRate? Ele serve para “reexecutar” esse método a cada 10 segundos? Eu gostaria de só invocar o ‘populate’ quando houver o upload.
O método push de Stack vai acionar o método @Scheduled? Como ele sabe qual @Scheduled ele deve executar?

Hahaha, ah! Beleza, aonde eu posso encontrá-la? Aliás, eu posso encontrá-la e, caso seja viável, usá-la?

Bom se você precisa executar alguma tarefa somente em certas condições esse plugin ou o quartz não vai te ajudar, ele é feito para facilitar tarefas periódicas…

Sobre o código acima ele empilha os arquivos que você quer processar, e a cada 10s processa o mais recente (você poderia fazer um loop e processar todos)
A biblioteca invoca o método automaticamente, se você quiser ter mais controle sobre a execução receba a classe TaskExecutor (execute, pause, resume…)

Para o seu caso eu indicaria a API do Java mesmo http://programmingexamples.wikidot.com/executorservice

na verdade era uma classe assim:

@Component
@PrototypeScoped
public class AtomicDAO {
    
     public AtomicDAO(SessionFactory factory) {
          this.factory = factory;
     }
     public void save(Object object) {
         Session session = factory.openSession();
         Transaction tx = null
         try {
               tx = session.beginTransaction();
               session.save(object);
               tx.commit();
         } catch (Exception e) {
              it (tx != null) {
                  tx.rollback();
              }
         } finally {
             session.close();
         }
     
     }
}

e vc recebe esses caras nas tasks

Beleza pessoal!

Esse negócio da Classe ApplicationScoped e do Executor funcionaram perfeitinho.

Só que apareceu um problema agora:
Quando o arquivo é muito grande, e eu estou fazendo alguns imports no banco de dados, depois de um tempo ele reclama que a transaction não foi inicializada…

Eu não implementei esse AtomicDAO, mas seria esse o problema?

tente algo parecido com esse atomic dao… talvez controlar transaçoes do lado da task pra ir mais rapido

Bom dia pessoal, aproveitando esse post eu queria a ajuda de vcs.
Estou tentando utilizar o task do vraptor mas não estou conseguindo. No console o logo fica rodando no tempo que eu determinei para o metodo ser chamado mas ele não é chamado, inclusive ele escreve a URL que era pra estar sendo chamada.
Estou usando a versão 4 de ambos.

o codigo é o seguinte:

[code]@Controller
public class UsuarioController {

    @Post
@Scheduled(fixedRate = 5000)
public void enviarEmail(){
	cronEnviarEmail.executeEmail(mensagem, assunto, destinatario, context);
}

}[/code]

[code]@RequestScoped
public class CronEnviarEmail{

public void executeEmail(String mensagem, String assunto, String destinatario, ServletContext context) {
	int dia = Integer.parseInt(DateUtil.getDia(new Date()));
	System.out.println("testando dia: " + dia);
	if(dia == 25){
		Mail email = new Mail();
		email.enviarEmail(mensagem, assunto, destinatario, context);
	}
}

}[/code]

e no console recebo isso

11:17:07,310 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:17:07,320 INFO [DefaultRequestScopedTask] Task returned status code 302 11:17:12,311 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:17:12,318 INFO [DefaultRequestScopedTask] Task returned status code 302 11:17:17,310 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:17:17,321 INFO [DefaultRequestScopedTask] Task returned status code 302 11:17:22,311 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:17:22,322 INFO [DefaultRequestScopedTask] Task returned status code 302 11:17:27,311 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:17:27,323 INFO [DefaultRequestScopedTask] Task returned status code 302 11:17:32,311 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:17:32,322 INFO [DefaultRequestScopedTask] Task returned status code 302 11:17:37,311 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:17:37,322 INFO [DefaultRequestScopedTask] Task returned status code 302 11:17:42,312 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:17:42,323 INFO [DefaultRequestScopedTask] Task returned status code 302 11:17:47,311 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:17:47,322 INFO [DefaultRequestScopedTask] Task returned status code 302 11:17:52,311 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:17:52,324 INFO [DefaultRequestScopedTask] Task returned status code 302 11:17:57,311 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:17:57,322 INFO [DefaultRequestScopedTask] Task returned status code 302 11:18:02,312 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:18:02,333 INFO [DefaultRequestScopedTask] Task returned status code 302 11:18:07,311 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:18:07,325 INFO [DefaultRequestScopedTask] Task returned status code 302 11:18:12,312 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:18:12,323 INFO [DefaultRequestScopedTask] Task returned status code 302 11:18:17,311 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:18:17,321 INFO [DefaultRequestScopedTask] Task returned status code 302 11:18:22,313 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:18:22,323 INFO [DefaultRequestScopedTask] Task returned status code 302 11:18:27,310 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:18:27,321 INFO [DefaultRequestScopedTask] Task returned status code 302 11:18:32,312 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:18:32,322 INFO [DefaultRequestScopedTask] Task returned status code 302 11:18:37,310 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:18:37,322 INFO [DefaultRequestScopedTask] Task returned status code 302 11:18:42,312 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:18:42,322 INFO [DefaultRequestScopedTask] Task returned status code 302 11:18:47,310 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:18:47,321 INFO [DefaultRequestScopedTask] Task returned status code 302 11:18:52,311 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:18:52,322 INFO [DefaultRequestScopedTask] Task returned status code 302 11:18:57,310 INFO [DefaultRequestScopedTask] Executing task in URL http://localhost:8080/consultaimpressao/usuario/enviarEmail 11:18:57,321 INFO [DefaultRequestScopedTask] Task returned status code 302

alguém poderia me ajudar? porque que esse método só é chamado uma vez?