FJ28 - Dao -> como acessar o dao dentro duma task schedulada? @Resource @Component..?

Veja bem… fiz a apostila FJ28 e agora estou implementando novas coisas. Por exemplo envio de email automatico com o Vraptor-Tasks.

Aí vai minha dúvida:

Por exemplo, para o historicoController acessar o HistoricoDao, pelo que eu intendi, ele apenas anota a classe com @Resource e @Component, e tudo funciona perfeitamente:

[code]@Resource
public class HistoricosController {
private HistoricoDao dao;
private final Result result;

private Validator validator;

public HistoricosController(HistoricoDao dao, Result result,
        Validator validator) {
    this.dao = dao;
    this.result = result;
    this.setValidator(validator);
}

@Restrito
@Get("/historico")
public List<Historico> lista() {
    return dao.listaTudo();
}

public List<Historico> busca(String titular) {
    result.include("titular", titular);
    return dao.busca(titular);
} 

… . . . . .

@Component
public class HistoricoDao {

private final Session session;

public HistoricoDao(Session session) {
    this.session = session;
}

public void salva(Historico historico) {
    Transaction tx = session.beginTransaction();
    session.save(historico);
    tx.commit();
}

public void atualiza(Historico historico) {
    Transaction tx = session.beginTransaction();
    this.session.update(historico);
    tx.commit();
}


[/code]
Agora quero fazer uma busca a partir da classe schedulada pelo Vraptor-Tasks, e não tenho idéia de como fazer… Pensei que era soh fazer isso aqui oh:

[code]
@Resource
@ApplicationScoped
@Scheduled(fixedRate = 21600000, concurrent = false)
public class EmailLigacoes implements Task {

private HistoricoDao dao;

public EmailLigacoes(HistoricoDao dao) {
    this.dao = dao;
}

…[/code]

mas não funcionou… ele retorna o seguinte erro no start do server:

SEVERE: Exception starting filter vraptor org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'taskHandler': Unsatisfied dependency expressed through constructor argument with index 1 of type [java.util.List]: : Error creating bean with name 'emailLigacoes': Unsatisfied dependency expressed through constructor argument with index 0 of type [br.com.ajm.scdri.dao.HistoricoDao]: : Error creating bean with name 'historicoDao': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'historicoDao': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'emailLigacoes': Unsatisfied dependency expressed through constructor argument with index 0 of type [br.com.ajm.scdri.dao.HistoricoDao]: : Error creating bean with name 'historicoDao': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'historicoDao': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request. at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:730) at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:196) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1003) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:907) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:580) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:895) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:425) at br.com.caelum.vraptor.ioc.spring.SpringBasedContainer.start(SpringBasedContainer.java:106) at br.com.caelum.vraptor.ioc.spring.SpringProvider.start(SpringProvider.java:87) at br.com.caelum.vraptor.VRaptor.init(VRaptor.java:108) at br.com.caelum.vraptor.VRaptor.init(VRaptor.java:102) at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:281) at org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:262) at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:107) at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4656) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5309) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549) at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source) at java.util.concurrent.FutureTask.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source)

OU SERIA MELHOR no construtor colocar a classe Controller invés do DAO???

quero fazer um simples dao.Busca(new Date());

e então???.. =)

Eu não entendo de VRaptor para esses casos de task, mas conforme for, se quiser resolver rapidamente, instancie seu próprio DAO em memória.

o “problema” de fazer isso é que eu teria que passar uma session como parametro . . . . e antes criar uma session…

não sei se seria a melhor das opções… em ultimo caso farei isso msmo…
acho que eu estaria fugindo do padrão … deve ter um eskema melhor …

o “problema” de fazer isso é que eu teria que passar uma session como parametro . . . . e antes criar uma session…

não sei se seria a melhor das opções… em ultimo caso farei isso msmo…
acho que eu estaria fugindo do padrão … deve ter um eskema melhor …[/quote]

Sim estaria saindo do padrão da ferramenta, mas resolveria seu problema…basta montar sua propria Session, é simples.

o “problema” de fazer isso é que eu teria que passar uma session como parametro . . . . e antes criar uma session…

não sei se seria a melhor das opções… em ultimo caso farei isso msmo…
acho que eu estaria fugindo do padrão … deve ter um eskema melhor …[/quote]

Sim estaria saindo do padrão da ferramenta, mas resolveria seu problema…basta montar sua propria Session, é simples.[/quote]

Obrigado mano!! por enquanto fiz isso ai mesmo! … Mas vamos ver ser os manos da Caelum ou alguem que manja de Vraptor da uma dica usando as ferramentas dele…

Abrçs

Tive esse problema com tarefas agendadas tb, mas eu estava usando o Quartz.

Na classe EmailLigacoes no metodo que executa as tarefas agendadas você pode charmar uma url:

URL url = new URL("http://localhost/suaaplicacao/minhaTarefa");
url.getContent();

E na classe HistorioController

    @Get("/minhaTarefa")  
    public void metodoQueExecutaSuaLogica() {  


    } 

[quote=orlandogpe]Tive esse problema com tarefas agendadas tb, mas eu estava usando o Quartz.
Na classe EmailLigacoes no metodo que executa as tarefas agendadas você pode charmar uma url:

URL url = new URL("http://localhost/suaaplicacao/minhaTarefa");
url.getContent();

E na classe HistorioController

    @Get("/minhaTarefa")  
    public void metodoQueExecutaSuaLogica() {  
    } 

[/quote]

Então manow… essa parada do Vraptor usa o Quartz por baixo dos panos! …
Amanha pela manhã vou tentar entender melhor essa parada que vc dise e se eu não conseguir retorno aqui… se eu conseguir eu retorno tbem … vlw!!

[quote=orlandogpe]Tive esse problema com tarefas agendadas tb, mas eu estava usando o Quartz.
Na classe EmailLigacoes no metodo que executa as tarefas agendadas você pode charmar uma url:

URL url = new URL("http://localhost/suaaplicacao/minhaTarefa");
url.getContent();

E na classe HistorioController

@Get("/minhaTarefa") public void metodoQueExecutaSuaLogica() { } [/quote]

Orlando, funcionou mano!! porém depois que eu colocar online, não será mais localhost!!
No meu caso usei

No JSP por exemplo, da pra usar a expressão <C:URL …> … no JAVA não tem??

Não esqueça de responder tbem como ficará quando eu hospedar!!!

ahh… percebi tbem que no CONSOLE ele joga uma msg de erro por não existir a jsp da url chamada pelo job… tem uma forma dele não tentar chamar nenhuma pagina com o “result.redirect” ou algo do tipo?

java.io.FileNotFoundException: http://localhost:8080/scdri/ligacoes at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source) at java.net.URLConnection.getContent(Unknown Source) at java.net.URL.getContent(Unknown Source) at br.com.ajm.scdri.email.EmailLigacoes.enviaEmailSimples(EmailLigacoes.java:42) at br.com.ajm.scdri.email.EmailLigacoes.execute(EmailLigacoes.java:31) at br.com.caelum.vraptor.tasks.jobs.simple.StatefulJobWrapper.execute(StatefulJobWrapper.java:24) at org.quartz.core.JobRunShell.run(JobRunShell.java:213) at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:557)
Pra resolver esse erro ai de cima, apenas criei uma pagina em branco dentro da pasta JSP com o nome do meu método… ai parou de sair esse erro… mas acredito que o servidor ao processar esse metodo tenta chamar essa url… doido ne?..
VLW!!!
Abrç´s

Opa, quando você for colocar online e ja tiver seu dominio… é só você trocar a url na classe.

URL url = new URL("http://www.seudominio.com.br/suaaplicacao/minhaTarefa");  
url.getContent();

Isso funciona como uma requisição http. É a mesma coisa de você colocar esse endereço no browser.

Para nao gerar essa erro eu tb criei uma pagina, tb pode jogar dentro de um try catch… mas eu criei uma pagina pq se eu quiser executar a tarefa manualmente … só jogo a url no browser.

Caso quiser deixar mais seguro, na sua classe voce pode passar um código como parametro.
Ex:

URL url = new URL("http://www.seudominio.com.br/suaaplicacao/minhaTarefa?code=123456789");  
url.getContent();

E na classe HistorioController

@Get("/minhaTarefa") public void metodoQueExecutaSuaLogica(String code) { if(!code.equals("123456789")){ return; } //sua logica }
Nesse caso se alguem conhecer essa URL e tentar fazer uma requisição via browser, seu metodo nao vai ser executado, a não ser que passar o codigo correto como parametro.

Essa não é a melhor solução, mas funciona, peguei essa dica com um dos desenvolvedores do vraptor.

Abraço

Na verdade tem um jeito muito mais fácil e bonito de fazer isso…

Vou primeiro te explicar por que esse problema ocorre:
O seu DAO está anotado com @Component, se você olhar na apostila, ele explica que quando não informamos o escopo, o VRaptor vai considerar @RequestScoped, ou seja, é preciso um request do usuário para que o VRaptor instancie essa sua classe.

A sua task EmailLigacoes está anotada com @ApplicationScoped, ou seja, começa a funcionar quando a sua aplicação inicia e fica correndo em paralelo, as classes anotadas com @ApplicationScoped e @SessionScoped não podem receber dependências @RequestScoped pois elas não precisam de um request para funcionar.

O seu component factory que faz a Session do Hibernate também não está anotado com o tipo do escopo e, por isso o VRaptor considera @RequestScoped.

Repare que o seu controller também não está anotado com o tipo do escopo e o VRaptor considera @RequestScoped.

Essas duas classes estão corretas, pois elas só podem funcionar quando tivermos um escopo.

Agora para resolver o problema, o wpivotto pensou nisso e falou: “Vou simular uma request, assim consigo pegar os @RequestScoped

Para isso, basta você criar um Controller, uma action e anotá-la com @Scheduled(fixedRate = 21600000, concurrent = false)…

Fica mais ou menos assim:

@Resource
public class EmailLigacoesController {
   private HistoricoDao dao;
   public EmailLigacoesController(HistoricoDao dao) {
      this.dao = dao;
   }

   @Scheduled(fixedRate = 21600000, concurrent = false)
   public void mandaEmail(){
      // Faz o código que precisa ser feito
   }
}

Com isso, o VRaptor Task vai simular um request e o VRaptor vai conseguir instanciar tudo para você.

Qualquer problema mande ai!

[quote=orlandogpe]Opa, quando você for colocar online e ja tiver seu dominio… é só você trocar a url na classe.

URL url = new URL("http://www.seudominio.com.br/suaaplicacao/minhaTarefa");  
url.getContent();

[/quote]
Isso vai se transformar em um pesadelo. Nunca escreva o domínio da aplicação…

Para fazer o mesmo que o <c:url> da JSP faz, na verdade você pode fazer isso:

public ConstrutorDoComponentDoVraptor(ServletContext context) { // O VRaptor injeta ele para você
   String app = context.getContextPath(); // Isso só vai trazer o nome da sua aplicação. Para pegar toda a URL vc precisa de um HttpServletRequest
}