[RESOLVIDO] MVC|DAO :: Problema de rechamada de DAO por service: Conexão fecha inesperadamente

Seguinte, estou construindo um sistema baseado em MVC, que responde as chamadas http com dados em XML ou um xml de erro caso haja uma exceção.

Com uma de minhas classes, estou tendo um problema na RECHAMADA do método (F5 no navegador), o que causa uma timeout na conexão do MySQL e uma exceção por tentativa de usar uma conexão fechada… modifiquei o design do método mas nada…

Confiram o a classe:

package br.com.tdkom.mailcfg.web.services;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.mysql.jdbc.Connection;

import br.com.tdkom.mailcfg.exceptions.MailCfgException;
import br.com.tdkom.mailcfg.interfaces.IModel;
import br.com.tdkom.mailcfg.web.Interfaces.IBusinessLogic;
import br.com.tdkom.mailcfgCustom.business.dao.AliasDAO;
import br.com.tdkom.mailcfgCustom.business.dao.ContaDAO;
import br.com.tdkom.mailcfgCustom.business.dao.DominioDAO;
import br.com.tdkom.mailcfgCustom.model.Alias;
import br.com.tdkom.mailcfgCustom.model.Conta;
import br.com.tdkom.mailcfgCustom.model.Dominio;

public class AliasContaService implements IBusinessLogic {

	@Override
	public void execute(HttpServletRequest request, HttpServletResponse response)throws IOException, Exception {
		
		if(MailCfgService.validarSessao(request, response)){
			try{
				
				HttpSession sessao = request.getSession();
				
				String acao = (String)request.getParameter("acao");
				
				if(acao.equals("set")){
					/* 
					 * Parametros:
					 * String acao = set
					 * long idConta
					 * long idDominio
					 * String aliases
					 * 
					 */
					AliasDAO aliasDAO = new AliasDAO((Connection)sessao.getAttribute("conexaoCustom"));
					
					ContaDAO contaDAO = new ContaDAO((Connection)sessao.getAttribute("conexaoCustom"));
					Conta conta = (Conta)contaDAO.getPorId(Long.parseLong(request.getParameter("idConta")));
					if(conta == null) throw new Exception("Conta não encontrada.");
					
					DominioDAO dominioDAO = new DominioDAO((Connection)sessao.getAttribute("conexaoCustom"));
					Dominio dominio = (Dominio)dominioDAO.getPorId(Long.parseLong(request.getParameter("idDominio")));
					if(dominio == null) throw new Exception("Domínio não encontrado.");
										
					
					// obtemos todos os alias dentro de uma String
					String aliasesRecebidos = request.getParameter("aliases");
					
					// vamos separar cada um
					String[] aliasSeparados = aliasesRecebidos.split(";");
					
					// criamos outra lista para colocar somente os nomes
					// em seguida, precisamos verificar a existência, caso verdadeira
					// removeremos.
					// Após toda separação e verificação, inserimos cada alias
					// com sua respectiva conta (destino) e domínio 
					List<String> nomeAlias = new ArrayList<String>();
					for(String nomeCadaAlias : aliasSeparados){
						nomeAlias.add(nomeCadaAlias.split("@")[0]);
					}
					// removendo todos os Alias da conta
					aliasDAO.remover((IModel)conta);
					
					// adicionando os alias
					for(int x=0;x < nomeAlias.size(); x++){						
						Alias alias = new Alias();
						alias.setAtivo(true);
						alias.setAlias(nomeAlias.get(x));						
						aliasDAO.inserir(alias, conta, dominio);
					}
				}else if(acao.equals("get")){
					/* 
					 * Parametros:
					 * String acao = get
					 * long idConta
					 * long idDominio
					 * 
					 */
					ContaDAO contaDAO = new ContaDAO((Connection)sessao.getAttribute("conexaoCustom"));
					Conta conta = (Conta)contaDAO.getPorId(Long.parseLong(request.getParameter("idConta")));
					if(conta == null) throw new Exception("Conta não encontrada.");		
					
					DominioDAO dominioDAO = new DominioDAO((Connection)sessao.getAttribute("conexaoCustom"));
					Dominio dominio = (Dominio)dominioDAO.getPorId(Long.parseLong(request.getParameter("idDominio")));
					if(dominio == null) throw new Exception("Dominio não encontrado.");		
					
					System.out.println("selecionando a alias: passo 1");
					AliasDAO aliasDAO = new AliasDAO((Connection)sessao.getAttribute("conexaoCustom"));
					System.out.println("selecionando a alias: passo 2");
					List<IModel> aliases = aliasDAO.getTodos((IModel)conta);
					
					StringBuilder xml = new StringBuilder();
					
					xml.append("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n");
					xml.append("<aliases>\n");				
					for(IModel objeto : aliases){
						Alias aliasObj = (Alias)objeto;
						xml.append(new StringBuilder().append("\t<alias>").append(aliasObj.getAlias()).append("@").append(dominio.getDominio()).append("</alias>\n").toString());
					}
					xml.append("</aliases>");
					response.setContentType("text/xml");
					PrintWriter out = response.getWriter();
					out.println(xml.toString());
					
				}else{
					request.setAttribute("descErro", "Parâmetro ACAO não foi passado para o sistema.");
					IBusinessLogic webException = new MailCfgException();
					webException.execute(request, response);
				}
			}catch(Exception e){
				request.setAttribute("descErro", "Ocorreu um erro ao configurar Alias da conta: "+e.getMessage());
				IBusinessLogic webException = new MailCfgException();
				webException.execute(request, response);
			}
		}else{
			request.setAttribute("descErro", "É necessário efetuar o login de usuário.");
			IBusinessLogic webException = new MailCfgException();
			webException.execute(request, response);
		}		
	}
}

O problema acontece quando estou executando a ação GET… em uma primeira chamada, exibe normalmente os dados. Efetuando um F5 ele já causa a excessão e exibe a mensagem:

<?xml version="1.0" encoding="iso-8859-1" ?> 
- <erro>
  <desc>Ocorreu um erro ao configurar Alias da conta: Conta não encontrada.</desc> 
  </erro>

Isso claro, por que o ContaDAO não retornou a conta como previsto.

Confiram o ContaDAO e o DominioDAO (não importa a sequencia que são dipostas causam o mesmo erro…):

ContaDAO.getPorId(int id)

package br.com.tdkom.mailcfgCustom.business.dao;

import br.com.tdkom.mailcfg.interfaces.ICrud;
import br.com.tdkom.mailcfg.interfaces.IModel;
import br.com.tdkom.mailcfgCustom.model.Conta;
import br.com.tdkom.mailcfgCustom.model.Dominio;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class ContaDAO implements ICrud
{
	private Connection conexao;
	
    public ContaDAO(Connection conexao)
    {
    	this.conexao = conexao;
    }	  

    public IModel getPorId(long id) throws SQLException
    {
    	try{
	        PreparedStatement estadoSql = conexao.prepareStatement("SELECT * FROM contas WHERE id=?");
	        estadoSql.setLong(1, id);
	        ResultSet resultado = estadoSql.executeQuery();
	        Conta conta = null;
	        if(resultado.next())
	        {
	            conta = new Conta();
	            conta.setId(resultado.getLong("id"));
	            conta.setConta(resultado.getString("conta"));
	            conta.setSenha(resultado.getString("senha"));
	            conta.setDtCriacao(resultado.getDate("dtCriacao"));
	        }
	        estadoSql.close();
	        return conta;
    	}catch(SQLException e){
    		System.out.println("<SQLException> Erro ao pesquisar conta por id: "+e.getMessage());
    	}catch(Exception e){
    		System.out.println("<Exception> Erro ao pesquisar conta por id: "+e.getMessage());
    	}
        return null;
    }
}

DominioDAO.getPorId(int id)

package br.com.tdkom.mailcfgCustom.business.dao;

import br.com.tdkom.mailcfg.interfaces.ICrud;
import br.com.tdkom.mailcfg.interfaces.IModel;
import br.com.tdkom.mailcfgCustom.model.Dominio;
import br.com.tdkom.mailcfgCustom.model.Usuario;


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class DominioDAO implements ICrud
{
	private Connection conexao;
	
    public DominioDAO(Connection conexao)
    {
    	this.conexao = conexao;
    }

    public IModel getPorId(long id) throws SQLException
    {
    	try{
	        System.out.println("Executando SELECT de dominio por id...");
	        PreparedStatement estadoSql = conexao.prepareStatement("SELECT * FROM dominios WHERE id=?");
	        estadoSql.setLong(1, id);
	        ResultSet resultado = (ResultSet) estadoSql.executeQuery();
	        Dominio dominio = null;
	        if(resultado.next())
	        {
	            dominio = new Dominio();
	            dominio.setId(resultado.getLong("id"));
	            dominio.setDominio(resultado.getString("dominio"));
	            dominio.setAtivo(resultado.getBoolean("status"));
	            dominio.setContasEmail(resultado.getLong("contasemail"));
	            dominio.setCotaEmail(resultado.getLong("cotaemail"));
	            dominio.setFiltro(resultado.getBoolean("filtro"));
	            dominio.setDtCriacao(resultado.getDate("dtcriacao"));
	        }
	        estadoSql.close();
	        return dominio;
    	}catch(SQLException e){
			System.out.println("Erro ao selecionar dominio por id: "+e.getMessage());
		}catch(Exception e){
			System.out.println("Erro ao selecionar dominio por id: "+e.getMessage());
		}
        return null;
    }
}

O web.xml faz uma chamada ao SessionListener o qual cria a conexão e a dispõe na sessão (há mais conexões nesta mesma classe, mas é irrelevande para o problema):

package br.com.tdkom.mailcfg.web.listeners;

import java.sql.DriverManager;
import java.sql.SQLException;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import com.mysql.jdbc.Connection;

public class SessionListener implements HttpSessionListener {
	
	private Connection conexaoCustom;
	
	public void sessionCreated(HttpSessionEvent event) {
		try{
			HttpSession sessao = event.getSession();
			
			// abrir as conexoes
			abrirConexaoCustom();
			
			sessao.setAttribute("conexaoCustom", conexaoCustom);
			
			sessao.setMaxInactiveInterval(10800);
			
		}catch(Exception e){
			System.out.println("Ocorreu um erro inesperado: "+e.getMessage());
		}
	}

	public void sessionDestroyed(HttpSessionEvent event) {
		if(!conexaoCustom.isClosed()){
			try {
				conexaoCustom.close();
			} catch (SQLException e) {
				System.out.println("Erro ao fechar a conexao CUSTOM: "+e.getMessage());
				e.printStackTrace();
			}catch(Exception e){
				System.out.println("Ocorreu um erro inesperado: "+e.getMessage());
			}
		}		
	}

	
	private void abrirConexaoCustom(){
		if(conexaoCustom == null){
			try{
				
				Class.forName("com.mysql.jdbc.Driver");
				System.out.println("Conectando-se com o banco Custom...");
				
				conexaoCustom = (com.mysql.jdbc.Connection) DriverManager.getConnection("jdbc:mysql://localhost/mailcfg_custom","*****","*****");
				
			}catch(ClassNotFoundException e){
				System.out.println("A classe do banco de dados não foi encontrada.");
			}catch(SQLException e){
				System.out.println("Erro ao conectar com a base de dados: "+e.getMessage());
			}catch(Exception e){
				System.out.println("Ocorreu um erro inesperado: "+e.getMessage());
			}
		}
	}
}

E FINALMENTE… o StackTrace do erro:

13:32:26,642 INFO [STDOUT] Erro ao pesquisar contas por critério: No operations allowed after connection closed.

Connection was closed explicitly by the application at the following location:

** BEGIN NESTED EXCEPTION **

java.lang.Throwable

STACKTRACE:

java.lang.Throwable
at com.mysql.jdbc.Connection.close(Connection.java:1125)
at br.com.tdkom.mailcfgCustom.business.dao.AliasDAO.getTodos(AliasDAO.java:50)
at br.com.tdkom.mailcfg.web.services.AliasContaService.execute(AliasContaService.java:119)
at br.com.tdkom.mailcfg.web.controller.ControllerServlet.service(ControllerServlet.java:26)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:235)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:190)
at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:92)
at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.process(SecurityContextEstablishmentValve.java:126)
at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:70)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:330)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:829)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:598)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
at java.lang.Thread.run(Unknown Source)

** END NESTED EXCEPTION **

Espero que alguém possa me ajudar :lol:

Pessoal nem acredito: [PROBLEMA RESOLVIDO]

o AliasDAO estava fechando a conexão… não sei onde estava com a quebeça as 3 da manhã quando escrevi esta linda:

conexao.close();

ao inves de:

estadoSql.close();

enfim… :roll:

Abraço a todos. E mesmo assim, espero ter exposto um ponto de vista de design que possa ser útil a vocês, mais detalhes ou discutir sobre o assunto, estamos aí…