Então estou implementando um sistema usando o VRaptor e gostaria de saber como faço para que quando alguém fizer o cadastro, ele pega algumas informações do usuário e faz uma busca com essas informações em uma url específica e redireciona para a página de login, mas eu gostaria de saber como faço para que isso seja feito por uma thread em paralelo com o que o usuário fizer. Isso para que o sistema não fique esperando terminar essa requisição. Outra dúvida é como fazer para que ele faça essa busca na url, por exemplo de 10 em 10 minutos e toda vez que o servidor cair e voltar ele faça a busca imediatamente quando iniciar o tomcat.
É isso mesmo cara, obrigado pela resposta. Agora como que eu faço para iniciar uma classe junto com o tomcat? Tipo essa classe tem o método que faz a busca no site, e gostaria de saber como faço para que ela carregue junto com o tomcat em caso de queda de energia ou algo assim.
Já que você está utilizando o VRaptor, você pode anotar uma Classe como @ApplicationScoped e @Component,
e nela criar um metodo anotado com @PostConstruct.
Este metodo será executado logo que o VRaptor iniciar esta classe para deixa-la disponivel como um componente da Aplicação.
package br.com.myapp.infra;
import javax.annotation.PostConstruct;
import br.com.caelum.vraptor.ioc.ApplicationScoped;
import br.com.caelum.vraptor.ioc.Component;
@ApplicationScoped
@Component
public class InitApplication {
@PostConstruct
public void init() {
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Objeto Instanciado!");
}
}
Nao sei se é a melhor aplicação, mas é funcional.
Os colegas com mais experiencia podem dar seu aval ou sugestões de uma implementação mais robusta para estes casos de execução na inicialização do projeto.
agora voltando a questão da thread. Eu implementei da seguinte maneira: quando o usuario faz o cadastro ele redireciona para ObjectController que cria a thread e da o start e redireciona para o formulário de login. Assim o usuário não fica esperando o retorno do método search que demora um pouco. Segue o código abaixo.
UserController.java
@Resource
public class UsersController {
private final UserDAO dao;
private final Result result;
private final Validator validator;
private final WebUser webUser;
public UsersController(UserDAO dao, Result result, Validator validator, WebUser webUser) {
this.dao = dao;
this.result = result;
this.validator = validator;
this.webUser = webUser;
}
@Post @Path("/users")
public void add(User user) {
if (dao.existUser(user)) {
validator.add(new ValidationMessage("Email já existe", "user.email"));
}
validator.onErrorUsePageOf(UsersController.class).newUser();
dao.add(user);
result.redirectTo(ObjectsController.class).callsearch(user);
}
ObjectsController.java
@Resource
public class ObjectsController {
private final ObjectDAO dao;
private final Result result;
private final Validator validator;
private final SearchThread st;
public ObjectController(ObjectDAO dao,Result result, Validator validator, SearchThread st){
this.dao = dao;
this.result = result;
this.validator = validator;
this.st = st;
}
public void callsearch(User user){
Runnable runnable = this.st;
// Cria uma thread que recebe o comportamento do objeto runnable
Thread thread = new Thread(runnable);
// Inicia a thread
thread.start();
result.redirectTo(UsersController.class).loginForm();
}
SearchThread.java
@Component
public class SearchThread implements Runnable{
private final Session session;
private final ObjectDAO dao;
private final Result result;
public SearchThread(ObjectDAO dao, Session session, Result result){
this.session = session;
this.dao = dao;
this.result = result;
}
public void run(){
ArrayList<Object> objs = new ArrayList<Object>();
User userLast = null;
try {
userLast = loadLast();
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
objs = search(userLast);
for (Object obj : objs){
dao.save(obj);
}
}
public User loadLast() throws Exception {
Criteria criteria = this.session.createCriteria( User.class );
criteria.addOrder( Order.desc( "ID" ) ).setMaxResults(1);
return ( User ) criteria.uniqueResult();
}
public ArrayList<Submission> search(User user){
...
A thread é chamada direitinho, se que o problema é que quando vai salvar ele diz que a session está fechada e assim não guarda no banco! Como eu poderia resolver esse problema? Já tentei de várias formas e nada.
Exception in thread "Thread-12" org.hibernate.SessionException: Session is closed!
at org.hibernate.impl.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:72)
at org.hibernate.impl.SessionImpl.beginTransaction(SessionImpl.java:1347)
at tecnico.dao.ObjectDAO.save(ObjectDAO.java:24)
at br.com.myapp.SearchThread.run(SearchThread.java:50)
at java.lang.Thread.run(Thread.java:680)
PS: O problema é bem nessa linha: dao.save(obj) no run da SearchThread.
Então cara, eu não utilizo o Spring não. Tentei anotar o run com o @Transactional e ele reconhece um @Transaction que é um plugin jpa, seria esse? Se não, tem outra maneira sem Spring? Vlw
então, se vc usa o plugin do vraptor (pacote …util.hibernate) ele só controla as transações por requisição. Abre no começo e fecha no final da requisição.
como que eu faço o job, nessa questão da thread, para chamar por exemplo o adiciona do meu ObjectController? Eu já tentei fazer minha SearchThread anotada como @Resource como se fosse outro controller, mas quando faço a chamada de result.redirectTo(ObjectsController.class).adiciona(object); causa o seguinte erro:
Exception in thread "Thread-6" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'defaultLogicResult': 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.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:339)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:263)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1083)
at br.com.caelum.vraptor.ioc.spring.SpringBasedContainer.instanceFor(SpringBasedContainer.java:86)
at br.com.caelum.vraptor.core.DefaultResult.use(DefaultResult.java:57)
at br.com.caelum.vraptor.core.AbstractResult.redirectTo(AbstractResult.java:46)
at tecnico.SearchThread.run(SearchThread.java:61)
at java.lang.Thread.run(Thread.java:680)
Caused by: 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.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.request.AbstractRequestAttributesScope.get(AbstractRequestAttributesScope.java:40)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:325)
Qual o Escopo da sua classe SearchThread ??? receio que você não possa usar redirect se não for escopo de Request… no caso o escopo padrão de um Componente não anotado no VRaptor.
A partir do momento que você muda o escopo do seu componente, alguns recursos não estarão mais acessiveis naturalmente… pelo menos é oq dá a entender o erro…
Acho que também você deve rever se a abordagem adotada p/ esse caso de tentar fazer esses redirects ai (mesmo q fosse possivel) é aplicável.
[quote=guivirtuoso]Qual o Escopo da sua classe SearchThread ??? receio que você não possa usar redirect se não for escopo de Request… no caso o escopo padrão de um Componente não anotado no VRaptor.
A partir do momento que você muda o escopo do seu componente, alguns recursos não estarão mais acessiveis naturalmente… pelo menos é oq dá a entender o erro…
Acho que também você deve rever se a abordagem adotada p/ esse caso de tentar fazer esses redirects ai (mesmo q fosse possivel) é aplicável.
[/quote]
Então Guilherme, minha classe não está anotada, logo está em escopo padrão, ou seja, de request mesmo. Não entendo esse erro, olha a minha classe:
package mypkg;
//imports ..
@Resource
public class SearchThread implements Runnable{
private final Session session;
private final SubmissionDAO dao;
private final Result result;
public SearchThread(ObjectDAO dao, Session session, Result result){
this.session = session;
this.dao = dao;
this.result = result;
}
public void run(){
ArrayList<Object> objs = new ArrayList<Object>();
User userLast = null;
try {
userLast = loadLast();
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
objs = search(userLast);
for (Object obj : objs){
result.redirectTo(ObjectsController.class).adiciona(object);
}
}
//retorna o último usuario cadastrado
public User loadLast() throws Exception {
Criteria criteria = this.session.createCriteria( User.class );
criteria.addOrder( Order.desc( "ID" ) ).setMaxResults(1);
return ( User ) criteria.uniqueResult();
}
public ArrayList<Object> search(User user){
//Faz a busca que falei e retorna o Arraylist
Lucas, vim te agradecer pela resposta e colocar o post em Resolvido. Funcionou perfeitamente aqui. Me desculpe a demora na resposta pois tive uns problemas e agora que vim implementar essa funcionalidade. Valeu mesmo.
Lucas,
estou com problemas para implementar o código em background, que é a minha dúvida inicial. É simples, mas não estou conseguindo. Criei uma classe e estou querendo que ela faça a busca de 1 em 1 minuto, só que dá um erro no startup que eu não sei como resolver.
package mypkg;
//imports
@ApplicationScoped
@Component
public class ScheduledSearchThread implements Runnable{
private Session session;
public ScheduledSearchThread(Session session){
this.session = session;
}
@Override
@PostConstruct
public void run() {
for (int i =0;; i++) {
if(i == 6) break;
ArrayList<Object> objs = new ArrayList<Object>();
List<User> users = new ArrayList<User>();
users = loadAll();
for (User user : users) {
try {
objs = search(user);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for (Object obj : objs) {
Transaction tx = this.session.beginTransaction();
this.session.save(submission);
tx.commit();
}
}
System.out.println("-------Vou durmir 60 segundos!----------");
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public List<User> loadAll(){
return this.session.createCriteria(User.class).list();
}
public ArrayList<Object> search(User user) throws MalformedURLException, IOException{
//busca e retorna o array de objetos
E o erro que estou recebendo é esse:
23:20:20,488 INFO [BasicConfiguration ] Using class br.com.caelum.vraptor.ioc.spring.SpringProvider as Container Provider
23:20:20,508 INFO [DefaultSpringLocator] No application context found
23:20:20,619 INFO [WebAppBootstrapFactory] No static WebAppBootstrap found.
23:20:20,620 INFO [BasicConfiguration ] br.com.caelum.vraptor.scanning = null
07/11/2011 23:20:20 org.apache.catalina.core.StandardContext filterStart
GRAVE: Exception starting filter vraptor
java.lang.NoClassDefFoundError: com/thoughtworks/xstream/converters/SingleValueConverter
at br.com.caelum.vraptor.core.BaseComponents.<clinit>(BaseComponents.java:195)
at br.com.caelum.vraptor.scan.ScannotationComponentScanner.addVRaptorStereotypes(ScannotationComponentScanner.java:176)
at br.com.caelum.vraptor.scan.ScannotationComponentScanner.findStereotypes(ScannotationComponentScanner.java:141)
at br.com.caelum.vraptor.scan.ScannotationComponentScanner.scan(ScannotationComponentScanner.java:60)
at br.com.caelum.vraptor.scan.WebAppBootstrapFactory.create(WebAppBootstrapFactory.java:65)
at br.com.caelum.vraptor.ioc.spring.SpringProvider.start(SpringProvider.java:83)
at br.com.caelum.vraptor.VRaptor.init(VRaptor.java:110)
at br.com.caelum.vraptor.VRaptor.init(VRaptor.java:103)
at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:273)
at org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:254)
at org.apache.catalina.core.ApplicationFilterConfig.setFilterDef(ApplicationFilterConfig.java:372)
at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:98)
at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4584)
at org.apache.catalina.core.StandardContext$2.call(StandardContext.java:5262)
at org.apache.catalina.core.StandardContext$2.call(StandardContext.java:5257)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:680)
Caused by: java.lang.ClassNotFoundException: com.thoughtworks.xstream.converters.SingleValueConverter
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1678)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1523)
... 20 more
07/11/2011 23:20:20 org.apache.catalina.core.StandardContext startInternal
GRAVE: Error filterStart
07/11/2011 23:20:20 org.apache.catalina.core.StandardContext startInternal
GRAVE: Context [/tecnico] startup failed due to previous errors
Como corrijo isso? Ou tem outra forma mais simples de fazer essa busca com uma única thread em background?