Remover conexões EM ESPERA ao fazer undeploy no tomcat [RESOLVIDO]

Olá pessoal!

Estamos com um problema aqui na empresa quando efetuamos undeploy de aplicações no servidor. Trabalhamos com sistemas desktop + web service e precisamos remover todas as conexões do pool quando for feito undeploy no tomcat.
Antes a gente tinha o pool configurado via aplicação porém agora estamos com o pool configurado no tomcat e mesmo assim as conexões não são removidas.
Vou dar um exemplo do que acontece: fizemos deploy de um arquivo .war (que no nosso caso é o web service) no servidor e acessamos a aplicação. O pool funciona de forma eficaz, respeitando todas as configurações passadas no context.xml. Aí atualizamos a aplicação e fizemos undeploy da aplicação já existente e fizemos um novo deploy (isso tudo sem reiniciar o tomcat, pois não podemos reiniciá-lo a cada deploy). Todas as conexões do pool que ficaram em espera, continuam lá… ou seja… não são liberadas do pool após o undeploy.

Como faço para resolver esse problema?

Obs.: já tentamos de tudo… e removeAbandoned=“true” não adianta…

Obrigado pela ajuda de todos

esse pool ai é por war ou global do tomcat? nas minhas app os pools são por war e o pool é destruido no undploy.

é configurado no war… no context.xml e no web.xml… antes era só na aplicação mas de nenhuma das formas as conexões são liberadas

o estranho é que mesmo configurado todo o pool pelo tomcat, ao fazer undeploy as conexões continuam com o estado “IDLE”… e são essas as conexões que teriam que ser removidas… porém não são, e até que o tomcat seja restartado, todas conexões ficarão lá, mesmo que nunca sejam acessadas…

Oi erico…
Estranho isso…vou verificar aqui no meu projeto…

Oi Erico

Acabei de fazer o teste…e realmente o tomcat deixa pendurado sim…ele só remove as conexões do pool quando o servidor é parado.
Mas tenho usado o HOT-DEPLOY ha anos e nunca tive problemas…
No seu próximo deploy da mesma aplicação ele vai usar o pool ja aberto…normal
Qual o problema disso para vc?

aí é que está, no hot-deploy ele não ocupa essas conexões em espera…
digamos que eu tenha um máximo de 5 conexões em espera e todas elas já criadas e aguardando acessos, aí faço o undeploy e o deploy da aplicação e aí quando o sistema é aberto novamente o tomcat cria outras 5 novas conexões (deixando aquelas outras 5 em espera ainda que nunca serão usadas…)

e aí conforme vou fazendo deploys, as conexões vao aumentando sempre, e logo estoura o limite máximo de conexões do banco…

obrigado pela ajuda, Fernando

Vou testar aqui no ambiente de homologação para ver…

ok… muito obrigado pela ajuda!

Fico no aguardo e qualquer avanço que eu tiver eu posto aqui também…

Nâo deu tempo de eu fazer…mas acho que unica forma do pool ficar pendura no undeploy é quando no próprio undeploy acontecer exceptions…
Vc ja verificou se log depois do undeploy?
Normalmente acontece por falta de jar’s, etc…

o log do tomcat informa o seguinte no undeploy:

30/05/2011 17:37:32 org.apache.catalina.loader.WebappClassLoader clearThreadLocalMap GRAVE: A web application created a ThreadLocal with key of type [org.apache.axis.utils.XMLUtils.ThreadLocalDocumentBuilder] (value [org.apache.axis.utils.XMLUtils$ThreadLocalDocumentBuilder@66a96863]) and a value of type [org.apache.xerces.jaxp.DocumentBuilderImpl] (value [org.apache.xerces.jaxp.DocumentBuilderImpl@7c04703c]) but failed to remove it when the web application was stopped. To prevent a memory leak, the ThreadLocal has been forcibly removed. 30/05/2011 17:37:33 org.apache.catalina.startup.HostConfig checkResources INFO: Undeploying context [/Aplicacao]
esta mensagem é a mesma para todos os undeploys feitos…

O log esta ok sim…
Eu to achando estranho ele duplicar o pool a cada novo deploy…
Temos que descobrir o pq disso…
Estou montando um ambiente teste aqui na minha maquina para eu testar esse comportamento no meus war’s aqui.
Depois te dou um retorno… :wink:

bah… muito obrigado pela ajuda e atenção…
fico no aguardo e enquanto isso vou fazendo mais testes aqui…

valeu!

Ola Erico

Consegui descobrir seu problema…vou tentar te explicar rapidamente…
O tomcat realmente não fecha as conexões do datasource por 3 motivos:

  1. A especificação do web container não diz que o provedor de web container tem que fechar as conexões do datasouce.
  2. O tomcat não pode fechar pq ele não esta amarrado com nenhum provedor de datasource, mesmo ele venha configurado com o commons dbpc da apache, cada um esta livre para usar qualquer outro.
  3. O commons dbpc não libera as conexões no undeploy.

Nessa thread aqui https://issues.apache.org/bugzilla/show_bug.cgi?id=37262 é discutido justamente isso…e os administradores do tomcat disseram que não podem ficar colocando código proprietário para fechar o pool do provedor A, B, C, etc…alegando que o produto TOMCAT não esta e não ira ficar amarrado com nenhum provedor de datasouce especifico…ficando então a cargo do desenvolvedor da aplicação se responsabilidar por isso (uma vez que ele é o responsavel por escolher o seu provedor)

Então vc tem 2 caminhos para resolver seu problema…ou colocar um provedor de pool que libere as conexões para vc ou fazer na unha mesmo pq o commons não faz.
A solução é fácil usando o próprio commons mesmo…é so liberar as conexões no metodo destroy de um listener de contexto servlet. Eu ja testei e deu certo:

[code]import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.apache.tomcat.dbcp.dbcp.BasicDataSource;

public class ContextoListener implements ServletContextListener {

public void contextInitialized(ServletContextEvent ev) {
}

public void contextDestroyed(ServletContextEvent ev) {
	try {
		Context ic = new InitialContext();
		Context CONTEXT = (Context) ic.lookup("java:/comp/env");
		BasicDataSource ds = (BasicDataSource) CONTEXT.lookup("<NOME_DO_SEU_JNDI>");
		ds.close();
	} catch (Exception e) {
		e.printStackTrace();
	} 
}    

}
[/code]
Qualquer dúvida estou por aqui.
T+ :wink:

novamente muito obrigado pela ajuda! Esse é um problema que estamos enfrentando a um tempo aqui e ainda não tínhamos a solução porém agora com tua ajuda estamos perto de resolver.
Só acho que não entendi muito bem… onde ficaria esse ContextoListener? Quando ele seria executado?

Porque hoje tenho um método que retorna o dataSource:

private DataSource createDataSource(){
   try {
      InitialContext context = new InitialContext();
      dataSource = (DataSource)context.lookup("java:comp/env/"+getValue(JNDI_NAME));
      return dataSource;
   } catch (Exception e) {
      e.printStackTrace();
   }
   return null;
}

a destruição do dataSource ocorreria manualmente no método que utiliza este objeto ou na hora de devolver a conexão ao pool depois de utilizada?

códigos auto-explicativos abaixo:

              /**
	 * retorna uma conexao do pool
	 */
	public Connection getConnection() {
		try {
			return dataSource.getConnection();
		} catch (SQLException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	/**
	 * devolve a conexao ao pool
	 */
	public void releaseConnection(Connection con) {
		if (con != null) {
			try {
				con.close();
			} catch (SQLException e) {
				e.printStackTrace();
				con = null;
			}
		}
	}

mais uma vez muito obrigado! Sua ajuda está sendo fundamental pra nós.

Não…
Estamos falando de fechar todas as conexões do pool (não é devolver uma conexão bloqueada para uso). Devolver a conexão reservada para uso vc continua fazendo o indicado pela especificação…vc pega a conexão via datasouce.getConnection()…usa nos selects, inserts etc e devolve con.close()…isso continua o mesmo
Vc vai adicionar um listener que vai executado no deploy do war e no undeploy…
Siga ai o manual…

  1. Escreve a classe que eu sugeri, colocando seus valores de nomes JNDI
  2. Declara no web.xml o listener o nome da classe totalmente qualificado:

<listener> <listener-class> pacote.pacote.seupacote.ContextoListener </listener-class> </listener>

hmm… claro, pois aí ele vai liberar todas as conexões quando um deploy ou undeploy for realizado… acho que estou entendendo…

vou implementar aqui e aí posto o resultado…

Muito Obrigado, Fernando!

cara eu tentei aqui e não deu certo, as conexões continuam lá…

tive que importar 2 bibliotecas do tomcat pois o projeto padrão não continham as classes importadas nesta classe q vc montou. Porém o log do tomcat apresenta um erro estranho dizendo que um dos jars que eu importei não foi possível carregar :confused:
segue o log:

31/05/2011 17:58:48 org.apache.catalina.startup.HostConfig deployWAR
INFO: Deploying web application archive indumoveis.war
31/05/2011 17:58:49 org.apache.catalina.loader.WebappClassLoader validateJarFile
INFO: validateJarFile(/var/lib/tomcat6/webapps/indumoveis/WEB-INF/lib/servlet-api.jar) - jar not loaded. See Servlet Spec 2.3, section 9.7.2. Offending class: javax/servlet/Servlet.class
31/05/2011 17:58:49 org.apache.axis.configuration.EngineConfigurationFactoryServlet getServerEngineConfig
GRAVE: Unable to find config file.  Creating new servlet engine config file: /WEB-INF/server-config.wsdd
31/05/2011 17:59:19 org.apache.axis.configuration.EngineConfigurationFactoryServlet getServerEngineConfig
GRAVE: Unable to find config file.  Creating new servlet engine config file: /WEB-INF/server-config.wsdd
java.lang.ClassCastException: org.apache.commons.dbcp.BasicDataSource cannot be cast to org.apache.commons.dbcp.BasicDataSource
	at server.database.control.ContextListener.contextDestroyed(ContextListener.java:18)
	at org.apache.catalina.core.StandardContext.listenerStop(StandardContext.java:4011)
	at org.apache.catalina.core.StandardContext.stop(StandardContext.java:4615)
	at org.apache.catalina.manager.ManagerServlet.undeploy(ManagerServlet.java:1365)
	at org.apache.catalina.manager.HTMLManagerServlet.undeploy(HTMLManagerServlet.java:563)
	at org.apache.catalina.manager.HTMLManagerServlet.doGet(HTMLManagerServlet.java:123)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
	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.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:558)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:859)
	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
	at java.lang.Thread.run(Thread.java:662)

outro detalhe estranho é o CastException que ocorre como mostrei acima também:

java.lang.ClassCastException: org.apache.commons.dbcp.BasicDataSource cannot be cast to org.apache.commons.dbcp.BasicDataSource

Obs.: as libs importadas no projeto são as mesmas libs do próprio tomcat…

Estranho mesmo…mas não essa classe ai…é outra ! Acho que vc importou errado…veja o exemplo que eu te mostrei:
org.apache.tomcat.dbcp.dbcp.BasicDataSource;
Tente denovo