Spring Mail com VRaptor 3

Oi gente! o/
Tô seguindo o manual do spring mail (http://static.springsource.org/spring/docs/2.5.x/reference/mail.html), mas além de estar mal explicado, manda colocar configuração no aplicationContext.xml. Como eu uso VRaptor 3 pensei numa forma melhor de usar isso através de DI.
Achei este tutorial, mas tb não me dá a pista de como fazer a integração do spring mail com Vraptor 3.
http://lucastex.com.br/category/spring/

Fiz aqui seguindo o manual:

public interface Email {
	
	void enviar(Contato contato);

}

E a classe Newsletter:

@Component
public class Newsletter implements Email  {
	
	private MailSender mailSender;
    private SimpleMailMessage templateMessage;
    private ImovelDAO imovelDAO;

    public void setMailSender(MailSender mailSender) {
        this.mailSender = mailSender;
    }

    public void setTemplateMessage(SimpleMailMessage templateMessage) {
        this.templateMessage = templateMessage;
    }
    
    public Newsletter(ImovelDAO imovelDAO) {
    	this.imovelDAO = imovelDAO;    	
    }

    public void enviar(Contato contato) {        

        // Create a thread safe "copy" of the template message and customize it        
        List <Imovel> imoveis = imovelDAO.listaDestaque();
        
        for (Imovel imovel : imoveis) {
            
        // Formatando a data de inclusao
        DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
        String dataInclusao = formatter.format(imovel.getDataInclusao());
             
        // Formatando o valor
        BigDecimal valor = imovel.getValor();
        NumberFormat nf = NumberFormat.getCurrencyInstance();
        
        
        SimpleMailMessage msg = new SimpleMailMessage(this.templateMessage);
        msg.setTo(contato.getEmail());
        msg.setSubject("Newsletter - Imobiliária");
        msg.setText(
        		"Código: " + imovel.getCodImovel() + "<br>" +            
                "Título: " + imovel.getTitulo() + "<br>" + 
                "Tipo: " + imovel.getTipo().getNome() + "<br>" + 
                "Modalidade: " + imovel.getModalidade().getTipoModalidade() + "<br>" +
                "Bairro: " + imovel.getEndereco().getBairro() + "<br>" + 
                "Estado: " + imovel.getEndereco().getEstado() + "<br>" +
                "Área: " + imovel.getArea() + "<br>" + 
                "Valor: " +  nf.format(valor) + "<br>" +
                "Quartos: " +  imovel.getQuartos() + "<br>" +
                "Data de inclusão: " + dataInclusao + "<br/><br/>"); 
        
        
        try{
            this.mailSender.send(msg);
        }
        catch(MailException ex) {
            // simply log it and go on...
            System.err.println(ex.getMessage());            
        }
    }

  }
}

Criando a pasta templates em WEB-INF/templates como posso integrar td com VRaptor sem usar o xml do spring?
Tipo, as configurações da conta:

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
  <property name="host" value="${mail.smtp.host}" />
  <property name="username" value="${mail.smtp.user}" />
  <property name="password" value="${mail.smtp.pass}" />
  <property name="port" value="${mail.smtp.port}" />
</bean>

Até tenho um aplicationContext.xml, mas será usado pro spring security, já não queria usá-lo pra não ficar muita coisa em xml, agora veio essa ai do spring mail pra atrapalhar. =/
Abraço!!

vc pode criar uma ComponentFactory de MailSender

algo assim:

@Component
//@ApplicationScope?
public class MailSenderFactory implements ComponentFactory<MailSender> {
      // instancia um JavaMailSenderImpl, seta as propriedades e guarda num field

      public MailSender getInstance() {
           return mailSender;
      }
}

Oi Lucas!!
Antes de usar o spring mail, tô fazendo o teste do scheduler com commons mail, só falta resolver este probleminha pra dar certo.
Está injetando nulo o DAO de contatos:

INFO: Server startup in 3910 ms
13:40:00,090 ERROR [JobRunShell         ] Job DEFAULT.schedulerJob threw an unhandled Exception: 
java.lang.NullPointerException
	at br.com.imobiliaria.quartz.SchedulerTask.run(SchedulerTask.java:40)
	at br.com.imobiliaria.quartz.SchedulerJob.executeInternal(SchedulerJob.java:28)
	at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:86)
	at org.quartz.core.JobRunShell.run(JobRunShell.java:216)
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:549)
13:40:00,092 ERROR [ErrorLogger         ] Job (DEFAULT.schedulerJob threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception. [See nested exception: java.lang.NullPointerException]
	at org.quartz.core.JobRunShell.run(JobRunShell.java:227)
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:549)
Caused by: java.lang.NullPointerException
	at br.com.imobiliaria.quartz.SchedulerTask.run(SchedulerTask.java:40)
	at br.com.imobiliaria.quartz.SchedulerJob.executeInternal(SchedulerJob.java:28)
	at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:86)
	at org.quartz.core.JobRunShell.run(JobRunShell.java:216)
	... 1 more
23/09/2010 13:40:36 org.apache.coyote.http11.Http11Protocol pause
INFO: Pausing Coyote HTTP/1.1 on http-8080
23/09/2010 13:40:37 org.apache.catalina.core.StandardService stop

A linha SchedulerTask.java:40 é esta:

List <Contato> contatos = contatoDAO.listaContatos();

A lista funciona pq estou usando em outras classes, já tentei anotar o costrutor com @Autowired e não deu certo, continua injetando nulo. =/
Sabe o que pode ser?
Abraço!!

quem instancia a task?

Está td no AplicationContext.xml:

<!-- Scheduler task -->
   <beans:bean name="schedulerTask" class="br.com.imobiliaria.quartz.SchedulerTask" />

<!-- Scheduler job -->
  <beans:bean name="schedulerJob" class="org.springframework.scheduling.quartz.JobDetailBean"> 
     <beans:property name="jobClass" value="br.com.imobiliaria.quartz.SchedulerJob" /> 
     <beans:property name="jobDataAsMap">
	    <beans:map>
	      <beans:entry key="schedulerTask" value-ref="schedulerTask" />
	     </beans:map>
      </beans:property>
   </beans:bean>	

<!-- Cron Trigger -->
	<beans:bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> 
		<beans:property name="jobDetail" ref="schedulerJob" />
		<beans:property name="cronExpression" value="0 * * * * ?" /> 
	</beans:bean>
	
	<!-- Scheduler -->
   <beans:bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
	<beans:property name="jobDetails">
	   <beans:list>
	      <beans:ref bean="schedulerJob" />
	   </beans:list>
	</beans:property>
 
	<beans:property name="triggers">
	    <beans:list>
		<beans:ref bean="cronTrigger" />
	    </beans:list>
	</beans:property>
   </beans:bean>

Ai tenho a classe Schedulertask, que é a classe que será executada:

public class SchedulerTask {
	
	private ImovelDAO imovelDAO;
	private ContatoDAO contatoDAO;
	
	public SchedulerTask() {
	}
	
	public SchedulerTask(ImovelDAO imovelDAO, ContatoDAO contatoDAO) {
		this.imovelDAO = imovelDAO;
		this.contatoDAO = contatoDAO;
	}
	
	public void run() throws MalformedURLException {
		
		
		HtmlEmail email = new HtmlEmail();
		
		List <Contato> contatos = contatoDAO.listaContatos(); // AQUI É O PROBLEMA  
		for(Contato contato: contatos) {
			
		try {
			email.setDebug(true);
			email.setHostName("smtp.gmail.com");			
			email.setFrom("eu@gmail.com", "Gerente");
			email.setAuthentication("eu","senha");  
			email.setSSL(true);  			
			email.addTo(contato.getEmail(), contato.getPessoa().getNome());
			email.setSubject("Newsletter - Imobiliária");

			// embed the image and get the content id
			URL url = new URL("http://www.tabetealmeida.com.br/_imagens/logo.png");
			String cid = email.embed(url, "Imobiliária logo");

			// set the html message
			List <Imovel> imoveis = imovelDAO.lista();	// AQUI É O PROBLEMA		
            StringBuilder html = new StringBuilder();
            html.append("<html><body><img src=\"cid:"+cid+"\"><br><body>Olá " + contato.getPessoa().getNome() +"!" + "<br>" +
            "Estamos enviando as últimas novidades, Confira!" + "<br>" +  "<br/>");
            for (Imovel imovel : imoveis) {
            
           // Formatando a data de inclusao
            DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
            String dataInclusao = formatter.format(imovel.getDataInclusao());
            
            // Formatando o valor
            BigDecimal valor = imovel.getValor();
            NumberFormat nf = NumberFormat.getCurrencyInstance();
            	
            html.append(
            "Código: " + imovel.getCodImovel() + "<br>" +            
            "Título: " + imovel.getTitulo() + "<br>" + 
            "Tipo: " + imovel.getTipo().getNome() + "<br>" + 
            "Modalidade: " + imovel.getModalidade().getTipoModalidade() + "<br>" +
            "Bairro: " + imovel.getEndereco().getBairro() + "<br>" + 
            "Estado: " + imovel.getEndereco().getEstado() + "<br>" +
            "Área: " + imovel.getArea() + "<br>" + 
            "Valor: " +  nf.format(valor) + "<br>" +
            "Quartos: " +  imovel.getQuartos() + "<br>" +
            "Data de inclusão: " + dataInclusao + "<br/><br/>");           
			}
            html.append("Att. <br/> Bruno - Gerente de vendas - =P"+"</body></html>");
            email.setHtmlMsg(html.toString());
			
			//enviando conteudo em HTML que vem por parametro
			//email.setContent("conteudo", "text/html");
			// set the alternative message
			email.setTextMsg("Seu servidor de e-mail não suporta mensagem HTML");
			// send the email
			email.send();
			
			
			
		} catch (EmailException e) {
			System.out.println(e.getMessage()); 
		}	
	}
  }
}

E no SchedulerJob:

public class SchedulerJob extends QuartzJobBean {
	
	private SchedulerTask schedulerTask;
	
	public SchedulerJob() {
	}
	
	public SchedulerJob(SchedulerTask schedulerTask) {
		this.schedulerTask = schedulerTask;
	}		
	 
	public void setSchedulerTask(SchedulerTask schedulerTask) {
		this.schedulerTask = schedulerTask;
	}
 
	protected void executeInternal(JobExecutionContext context)
	throws JobExecutionException {	
	
		try {
			schedulerTask.run(); // aqui executo o metodo de envio de emails
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
	
	}
}

Tanto a lista de contato como de imóveis está injetando nulo.
Abraço!

o problema é que vc tem dois construtores e o spring vai usar o sem argumentos, por isso que o dao fica nulll…

um problema é que os componentes registrados no VRaptor não são visíveis no Spring =*(

tem alguma forma programática de configurar o Spring Mail? ou programática ou via anotações?

Então, no spring é pelo xml mesmo, estou seguindo esta dica:
http://www.mkyong.com/struts2/struts-2-spring-quartz-scheduler-integration-example/

Eu tive que criar o construtor vazio pq ele acusava erro, dizendo que não existia o construtor, mesmo existindo o construtor preenchido, doidera. O.o
Aí tentei mandar uma anotação @Autowired no construtor preenchido, mas nem se abalou. =/

Mas ai descobri que o VRaptor 3 conseque startar uma task desde que quem implemente as interfaces esteja anotado com @Component e @ApplicationScoped:
http://vraptor.caelum.com.br/cookbook/job-scheduling-com-vraptor3-e-spring/

Vou tentar adaptar pro meu caso…

isso, era a minha proxima opção =)

Acho que têm alguma coisa errada no tutorial, a classe Runnable possui método run(), a classe que herda possui método schedule(), beleza, agora a classe MyFirstApplicationTask acusa:

The type MyFirstApplicationTask must implement the inherited abstract method Runnable.run()

Não está enxergando o método schedule() da classe Task. Ai neste método:

public void schedule(TaskScheduler scheduler) {
        scheduler.schedule(this, new CronTrigger("0 0 23 * * *")); // aqui
        //Neste caso, a task rodará sempre às 23h0min0s
    }

Manda esta mensagem:

The method schedule(Runnable, Trigger) in the type TaskScheduler is not applicable for the arguments (MyFirstApplicationTask, CronTrigger)

Agora ferrou, preciso sobrescrever esse método que está como Trigger para CronTrigger, como posso fazer isso Lucas?

MyFirstApplicationTask implementa Runnable?
deveria

Então, o autor fez uma herança lá, está assim:
Runnable << Task << ApplicationTask <-- MyFirstApplicationTask
Onde << é herança e <-- é implementação da interface. O component MyFirstApplicationTask deveria enxergar o método na interface Task:

public interface Task extends Runnable {
	void schedule(TaskScheduler scheduler);
}

Está ignorando este método e exigindo o método run() na Runnable. =/

tah compilando no eclipse mas qdo vc roda dah esse erro de compilação?

dá um clean no projeto e no servidor, e vê se roda

Continua a mensagem:

The method schedule(Runnable, Trigger) in the type TaskScheduler is not applicable for the arguments (Newsletter, CronTrigger)

O método schedule está esperando trigger, mas o autor da receita usou CronTrigger (que é o que eu queria usar mesmo), como ele conseguiu essa façanha?? O.o

Newsletter é um Runnable?
CronTrigger é um Trigger?

Veja o que o autor da receita postou:

@Component
@ApplicationScoped
public class MyFirstApplicationTask implements ApplicationTask {
    public MyFirstApplicationTask(TaskScheduler scheduler) {
        //Aqui você poderá receber componentes que não estejam em
        //escopo de request ou session
        ...
        this.schedule(scheduler);
    }
    public void schedule(TaskScheduler scheduler) {
        scheduler.schedule(this, new CronTrigger("0 0 23 * * *")); // aqui espera trigger e ele colocou CronTrigger.
        //Neste caso, a task rodará sempre às 23h0min0s
    }
}

A minha classe Newsletter:

@Component
@ApplicationScoped
public class Newsletter implements ApplicationTask {
	
	public Newsletter(TaskScheduler scheduler) {
        this.schedule(scheduler);
    }
	
    public void schedule(TaskScheduler scheduler) {
        scheduler.schedule(this, new CronTrigger("0 0 23 * * *")); 
        //Neste caso, a task rodará sempre às 23h0min0s
    }

}

Fiz igual… =/

[quote=Lucas Cavalcanti]Newsletter é um Runnable?
CronTrigger é um Trigger?[/quote]

Pergunta melhor: vc tah usando o CronTrigger do spring ou o do quartz?

tenta trocar

Refiz td de novo, o certo era:

Agora não entendi este construtor dentro do MyFirstRequestTask:

Criei essa classe e manda implementar o método run() tb, não tô entendendo com funciona isso não. Se já tenho a MyFirstApplicationTask, pra que essa outra?
Abraço!

o construtor é só pra dizer q vc pode receber qqer dependência

vc precisa implementar o método run, que é o que a sua task vai fazer de fato

o método schedule é só pra vc agendar sua task, entendeu?

Entendi, o que não entendi é pq ele colocou o nome do construtor diferente do nome da classe:

public class MyFirstRequestTask implements RequestTask {
public MyFirstApplicationTask() { 
}

Tá certo isso?

Quem vai de fato executar a minha classe de envio de email? MyFirstApplicationTask ou MyFirstRequestTask? Fiquei confuso com isso agora. O.o