Problema Componente java.lang.OutOfMemory[RESOLVIDO]

Meu caro,

Por favor, volte as configurações anteriores do Spring e nos faça um teste na EmailSendTask . De nada custa:

  • Use transaction.wasCommitted() no finally para dar o rollback;
  • Finalize a factory no finally : factory.close();
  • Dentro do catch (Exception e) :
    . Coloque tudo dentro de um novo try/catch/finally e declare uma variavel para PrintWriter:

PrintWriter p = null;
try {

} finally {
if (p!= null) { p.close(); }
}

Por favor, faça os testes e nos retorne o resultado. Se possível, inclua também o código fonte de SendMail.java

Deve ser algo assim:

@Component
@PrototypeScoped
public class EmailPrototypedDao {
	
	private Session session;
	
	public EmailPrototypedDao(SessionFactory sessionFactory) {
		this.session = sessionFactory.openSession();
	}
	public void update(EmailPanel emailPanel) {
		this.session.merge(emailPanel);
	}
	public List<EmailPanel> listAllNotSended() { 
		return suaQuery; 
	}
	protected Session getSession() {
		return session;
	}
}

e naEmailSendTask o método run:

	@Override
	public void run() {// aqui roda a aplicação
		List<EmailPanel> emails  = this.emailPrototypedDao.listAllNotSended();
		for(EmailPanel email : emails){  
                 try {  
                     SendMail.sendMail(email.getTo(),email.getFrom(), email.getSubject(), email.getMessage());  
                     email.setSended('S');  
                     email.setSendeddate(Calendar.getInstance().getTime());  
                     email.setProblem("");  
                 } catch(Exception e) {  
                     StringWriter w = new StringWriter();  
                     email.setSended('N');  
                     e.printStackTrace(new PrintWriter(w));  
                     email.setProblem(w.toString());  
                 }  
                 this.emailPrototypedDao.update(email);  
             }  
	}

inclua no inicio do código:

final private EmailPrototypeDao emailprototypeDao;

e inclua o EmailPrototypedDao no contrutor

[quote=Lagaffe]Deve ser algo assim:

@Component
@PrototypeScoped
public class EmailPrototypedDao {
	
	private Session session;
	
	public EmailPrototypedDao(SessionFactory sessionFactory) {
		this.session = sessionFactory.openSession();
	}
	public void update(EmailPanel emailPanel) {
		this.session.merge(emailPanel);
	}
	public List<EmailPanel> listAllNotSended() { 
		return suaQuery; 
	}
	protected Session getSession() {
		return session;
	}
}

e naEmailSendTask o método run:

	@Override
	public void run() {// aqui roda a aplicação
		List<EmailPanel> emails  = this.emailPrototypedDao.listAllNotSended();
		for(EmailPanel email : emails){  
                 try {  
                     SendMail.sendMail(email.getTo(),email.getFrom(), email.getSubject(), email.getMessage());  
                     email.setSended('S');  
                     email.setSendeddate(Calendar.getInstance().getTime());  
                     email.setProblem("");  
                 } catch(Exception e) {  
                     StringWriter w = new StringWriter();  
                     email.setSended('N');  
                     e.printStackTrace(new PrintWriter(w));  
                     email.setProblem(w.toString());  
                 }  
                 this.emailPrototypedDao.update(email);  
             }  
	}

inclua no inicio do código:

final private EmailPrototypeDao emailprototypeDao;

e inclua o EmailPrototypedDao no contrutor[/quote]

entendi agora o problema é que uso o daoEmail na aplicação em escopo de request tamem…

PrototypedScope atende o escopo de request???

[quote=CarvalR2]Meu caro,

Por favor, volte as configurações anteriores do Spring e nos faça um teste na EmailSendTask . De nada custa:

  • Use transaction.wasCommitted() no finally para dar o rollback;
  • Finalize a factory no finally : factory.close();
  • Dentro do catch (Exception e) :
    . Coloque tudo dentro de um novo try/catch/finally e declare uma variavel para PrintWriter:

PrintWriter p = null;
try {

} finally {
if (p!= null) { p.close(); }
}

Por favor, faça os testes e nos retorne o resultado. Se possível, inclua também o código fonte de SendMail.java
[/quote]

package br.com.webtia.contrato.scheduler;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Calendar;
import java.util.List;

import org.apache.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.joda.time.DateTime;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.support.CronTrigger;

import br.com.caelum.vraptor.ioc.ApplicationScoped;
import br.com.caelum.vraptor.ioc.Component;
import br.com.webtia.contrato.daos.EmailPanelDao;
import br.com.webtia.contrato.interfaces.ApplicationTask;
import br.com.webtia.contrato.models.EmailPanel;
import br.com.webtia.contrato.util.SendMail;

@Component
@ApplicationScoped
public class EmailSendTask implements ApplicationTask {
	static Logger logger = Logger.getLogger(EmailSendTask.class); 
	public EmailSendTask(TaskScheduler scheduler) {
    	this.schedule(scheduler);
    }
    public void schedule(TaskScheduler scheduler) {
    	scheduler.schedule(this, new CronTrigger("0/10 * * * * *"));
    }
	@Override
	public void run() {
		
		//Disparo de email
		long start = System.currentTimeMillis();
		Configuration conf = new AnnotationConfiguration().configure();
		SessionFactory factory = conf.buildSessionFactory();
		Session session = factory.openSession();
		Transaction transaction = null;
		try {
			transaction = session.beginTransaction();
			EmailPanelDao daoEmailPanel = new EmailPanelDao(session);
			List<EmailPanel> emails  = daoEmailPanel.listAllNotSended();
			for(EmailPanel email : emails){
				try {
					SendMail.sendMail(email.getTo(),email.getFrom(), email.getSubject(), email.getMessage());
					email.setSended('S');
					email.setSendeddate(Calendar.getInstance().getTime());
					email.setProblem("");
				} catch(Exception e) {
					PrintWriter p = null;
					try {
						email.setSended('N');
						StringWriter w = new StringWriter();
						p=new PrintWriter(w);
						e.printStackTrace(p);
						email.setProblem(w.toString());
					} finally {
						if (p!= null) { p.close(); }
					} 
				}
				daoEmailPanel.update(email);
			}
			
			transaction.commit();
		} finally {
			if (transaction.wasCommitted() && transaction.isActive()) {
				transaction.rollback();
			}
			session.close();
			factory.close();
		}
		long end = System.currentTimeMillis();
		logger.info("Task EmailSendTask executada em =>"+(end-start)+" ms - Ultima execução =>"+new DateTime());
	}
}

Bom segundo suas modificações ficaria assim como acima??

código do sendMail

package br.com.webtia.contrato.util;

import java.util.Properties;

import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import org.apache.log4j.Logger;


public class SendMail {
	public static void sendMail(String to,String from, String subject, String message) throws Exception {
		Logger logger=Logger.getLogger(SendMail.class);
		Properties props = new Properties();
		props.put("mail.smtp.host", "localhost");
		props.put("mail.smtp.auth", "true");
		props.put("mail.mime.charset", "ISO-8859-1");

		Session session = Session.getDefaultInstance(props,
				new Authenticator() {
					protected PasswordAuthentication getPasswordAuthentication() {
						return new PasswordAuthentication("usuario",
								"senha");
					}
				});
		Message msg = new MimeMessage(session);
		if(to.contains(",")){
			String mails[]=to.split(",");
			InternetAddress adresses[]=new InternetAddress[mails.length];
			for(int i=0;i<mails.length;i++){
				adresses[i]=new InternetAddress(mails[i]);
			}
			msg.setRecipients(Message.RecipientType.TO, adresses);
		} else msg.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
		if(from.equals("")) msg.setFrom(new InternetAddress("contratos@defensoria.sp.gov.br"));
		else msg.setFrom(new InternetAddress(from));
		msg.setSubject(subject);
		msg.setContent(message, "text/html");
		try {
			Transport.send(msg);
		} catch(Exception e) {
			logger.debug("Dados do email ---------------------");
			logger.debug("From : "+from);
			logger.debug("To : "+to);
			logger.debug("Subject : "+subject);
			logger.debug("Message : "+message);
			logger.debug("---------------------");
			logger.info("Não conseguiu disparar email "+e.getMessage());
		}
		
	}
}

Sim. Após estas alterações, como está o consumo ?

No livro de receitas está informando:

No livro de receitas está informando:

[quote]
O Spring possui um escopo chamado prototype que define que o componente terá uma nova instância sempre que for requisitada… Este escopo serve para definir compenentes utilizados por tasks em escopo de application que naturalmente não são singletons, como DAO?s.

Deste modo, será criado uma instância do seu DAO para cada requisição de instância (lê-se: sempre que você receber uma instância pelo construtor, será uma nova), assim como uma Session separada.
[/quote][/quote]

“Este escopo serve para definir compenentes utilizados por tasks em escopo de application”

entao como tinha dito anteriormente eu preciso desse muitas vezes no escopo de request em diversos ponto do programa nao vou recriar um componente pra cadas escopo pra repetir código…tem como falar que um componente pode estar em dois escopos ao mesmo tempo?? no caso no escopo prototyped e request??

@CarvalR2

Cara num é que era o close da factory mesmo…rodei o profiler aki de novo com as alterações…pq dificilmente ele iria entrar no exception do close do printwriter…agora com o profiler se mantem estavel em 108 megas…e 11 threads…
isso rodando de 10 em 10 segundos…o que seria pra estressar o ambiente mesmo…e a carga se mantem…

Obrigado pela paciencia e ajuda de voces…vo voltar o eskema pra rodar no produção com esse close que faltou…

Antes de minha barba ficar branca, eu aprendi isso com um amigo muito experiente:

  • Sempre procure metodos close(), quit(), finalize(), release() , etc nestes objetos de frameworks e na propria api java, quando os utilizar. Pois isso salva fim de semana.

(e vale muito para mes de copa do mundo)

Fico feliz em repassar esse conhecimento. Até +

[quote=CarvalR2]Antes de minha barba ficar branca, eu aprendi isso com um amigo muito experiente:

  • Sempre procure metodos close(), quit(), finalize(), release() , etc nestes objetos de frameworks e na propria api java, quando os utilizar. Pois isso salva fim de semana.

(e vale muito para mes de copa do mundo)

Fico feliz em repassar esse conhecimento. Até +[/quote]

É entao eu ja tinha mudado o eskema desse scheduler pra nao ficar de barba branca…kkkk…como disse pra uma maneira menos portavel mas que resolveu…

…mas sou de ir atras o porque nao estava funcionando…agora vou ficar mais esperto…com esse tipo de coisa ou seja quando eu “o programador burraldo fizer as coisas” realmente escapou akele close fiquei tão preocupado em fechar a session que esqueci da factory…

Valeu mesmo…

Cara, a partir de agora vc vai ficar tão de olho nisso, que nunca mais vai ter problema de leak. E se tiver, vai resolver rápido!

falowz

Mais importante que isso é ler a documentação ao invés de programar na base da tentativa e erro.

Já que o culpado inicial de tudo foi aquele componente que está no cookbook minha sugestão é retira-lo e notificar o autor. Logo vamos ter uma sindrome de “out-of-memory” de tanta gente fazendo copy and paste de código bugado.

Garcia,

Voce está falando do Job Scheduling com Vraptor e Spring que está no livro de receitas?

Eu tenho uma bagagem muito pequena em java, mas esse artigo me parece muito bom e pelo que vi o Boneazul implementou de uma forma diferente da recomendada no artigo. Poderia tentar testar a solução sugerida, estou com pouco tempo mas posso tentar agendar.

Se estiver equivocado, por favor me informe.

[quote=Lagaffe]Garcia,

Voce está falando do Job Scheduling com Vraptor e Spring que está no livro de receitas?

Eu tenho uma bagagem muito pequena em java, mas esse artigo me parece muito bom e pelo que vi o Boneazul implementou de uma forma diferente da recomendada no artigo. Poderia tentar testar a solução sugerida, estou com pouco tempo mas posso tentar agendar.

Se estiver equivocado, por favor me informe.[/quote]

Tá certo, acabei não lendo com atenção o tópico e falei besteira :oops:

Olhei com calma a sugestão do cookbook e realmente me parece que está tudo certo, visualmente falando. Os objetos que teriam problema em criar multiplas instâncias e não fechar estão todos como application-scoped. Me parece certo mesmo.

[quote=garcia-jj][quote=Lagaffe]Garcia,

Voce está falando do Job Scheduling com Vraptor e Spring que está no livro de receitas?

Eu tenho uma bagagem muito pequena em java, mas esse artigo me parece muito bom e pelo que vi o Boneazul implementou de uma forma diferente da recomendada no artigo. Poderia tentar testar a solução sugerida, estou com pouco tempo mas posso tentar agendar.

Se estiver equivocado, por favor me informe.[/quote]

Tá certo, acabei não lendo com atenção o tópico e falei besteira :oops:

Olhei com calma a sugestão do cookbook e realmente me parece que está tudo certo, visualmente falando. Os objetos que teriam problema em criar multiplas instâncias e não fechar estão todos como application-scoped. Me parece certo mesmo.[/quote]

o componente esta ok sim…o erro foi da implementacao que faltou fechar a Sessionfactory por isso dava o vazamento de memória…é q na epoca nao havia tambem o suporte a prototypedScope no vraptor…que hoje tem…

mas para o meu caso ainda ta complicado injetar o dao la dentro pq eu uso o dao tanto no escopo de request e o scheduler que fica no escopo de aplicação nao consegue injetar…

com eu faria pra injetar o dao nesse escopo sem ter que criar uma classe igualzinha nessse escopo…tem jeito??

[quote=boneazul]mas para o meu caso ainda ta complicado injetar o dao la dentro pq eu uso o dao tanto no escopo de request e o scheduler que fica no escopo de aplicação nao consegue injetar…

com eu faria pra injetar o dao nesse escopo sem ter que criar uma classe igualzinha nessse escopo…tem jeito??[/quote]

Não, você não pode injetar um requested-scope em um application scoped. Mas o contrário você pode.

Isso, a principio, me parece um erro de lógica. Mas pensando bem não vejo um problema nisso, e até o EJB permite você fazer isso, já que em um EJB Scheduler do 3.1 eu tenho stateless session beans injetados em um scheduler.

[quote]boneazul wrote:

mas para o meu caso ainda ta complicado injetar o dao la dentro pq eu uso o dao tanto no escopo de request e o scheduler que fica no escopo de aplicação nao consegue injetar…

com eu faria pra injetar o dao nesse escopo sem ter que criar uma classe igualzinha nessse escopo…tem jeito??
[/quote]

Tambem acho que não é possível. Fiquei com a mesma dúvida, mas me pareceu que qualquer solução ficaria mais complexa que duplicar o(s) dao(s). Pelo menos no sistema que estamos desenvolvendo aqui.

A observação do Garcia é interessante. Talvez permita uma solução mais simples.

[quote=Lagaffe][quote]boneazul wrote:

mas para o meu caso ainda ta complicado injetar o dao la dentro pq eu uso o dao tanto no escopo de request e o scheduler que fica no escopo de aplicação nao consegue injetar…

com eu faria pra injetar o dao nesse escopo sem ter que criar uma classe igualzinha nessse escopo…tem jeito??
[/quote]

Tambem acho que não é possível. Fiquei com a mesma dúvida, mas me pareceu que qualquer solução ficaria mais complexa que duplicar o(s) dao(s). Pelo menos no sistema que estamos desenvolvendo aqui.

A observação do Garcia é interessante. Talvez permita uma solução mais simples.[/quote]

A solução que voce dizem é passar o dao para @ApplicationScoped??

[quote=boneazul][quote=Lagaffe]Tambem acho que não é possível. Fiquei com a mesma dúvida, mas me pareceu que qualquer solução ficaria mais complexa que duplicar o(s) dao(s). Pelo menos no sistema que estamos desenvolvendo aqui.

A observação do Garcia é interessante. Talvez permita uma solução mais simples.[/quote]

A solução que voce dizem é passar o dao para @ApplicationScoped?? [/quote]

Pois é, não entendo bem qual a diferença, isso a nivel de dao, de ser application. Se pensar no EJB, um stateful não deixa de ser um application scoped. Mas isso o Lucas que pode opinar melhor.

O problema seria você ter um session como atributo de classe, porém creio que se a session segue a idéia do entity-manager isso não será problema.