Tengo una aplicación Java EE ejecutándose en Glassfish y conectándose a MSSQL Server 2008 mediante jTDS. Por alguna razón desconocida, la conexión de la base de datos se cierra inesperadamente durante las solicitudes. La aplicación es enorme, pero aquí hay un resumen de cómo ocurre el error:Conexión de la base de datos cerrada inesperadamente con Glassfish, jTDS y SQL Server 2008
Durante la instalación de Glassfish, con crear un grupo de conexión con asadmin create-jdbc-connection-pool
y asadmin create-jdbc-resource
. La clase de fuente de datos es net.sourceforge.jtds.jdbcx.JtdsDataSource
.
Cuando Glassfish sube, llama a nuestra implementación de ServletContextListener.contextInitialized(), donde buscamos el origen de datos de JNDI. El origen de datos se almacena en una variable estática.
Por un tiempo, todo va bien. Todas las solicitudes se manejan y no se cierra ninguna conexión. Nuestra aplicación realiza el procesamiento utilizando EJBs de temporizador y MDB (Bean controlado por mensajes).
Este es un ejemplo de implementación onMessage()
:
public void onMessage(Message message) {
this.message = message;
this.connection = dataSource.getConnection(userName, password);
try {
doQuery1();
doTransaction1();
doTransaction2();
doQuery2();
doQuery3();
} finally {
this.connection.close();
this.connection = null;
}
}
Con el tiempo, empezamos a obtener la siguiente excepción (que sucede alrededor de 100 veces durante una hora):
java.sql.SQLException: Invalid state, the Connection object is closed.
at net.sourceforge.jtds.jdbc.ConnectionJDBC2.checkOpen(ConnectionJDBC2.java)
at net.sourceforge.jtds.jdbc.ConnectionJDBC2.prepareStatement(ConnectionJDBC2.java)
at net.sourceforge.jtds.jdbc.ConnectionJDBC2.prepareStatement(ConnectionJDBC2.java)
at com.sun.gjc.spi.base.ConnectionHolder.prepareStatement(ConnectionHolder.java:475)
at com.acme.myejbs.MyMDB.doQuery2(MyMDB.java:123)
at com.acme.myejbs.MyMDB.onMessage(MyMDB.java:614)
at sun.reflect.GeneratedMethodAccessor115.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java)
at java.lang.reflect.Method.invoke(Method.java)
at com.sun.enterprise.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1011)
...
at $Proxy92.onMessage(Unknown Source)
at com.sun.messaging.jms.ra.OnMessageRunner.run(OnMessageRunner.java)
at com.sun.enterprise.connectors.work.OneWork.doWork(OneWork.java:77)
at com.sun.corba.ee.impl.orbutil.threadpool.ThreadPoolImpl$WorkerThread.run(ThreadPoolImpl.java:555)
La excepción ocurre en JDBC al azar llamadas. A veces ocurre durante la iteración ResultSet, otras veces durante la ejecución de la consulta.
En casos muy raros (7 veces durante una hora) obtenemos esta excepción:
java.sql.SQLException: Error in allocating a connection. Cause: This Managed Connection is not valid as the phyiscal connection is not usable
at com.sun.gjc.spi.base.DataSource.getConnection(DataSource.java:136)
at com.acme.myejbs.MyMDB.onMessage(MyMDB.java:614)
at sun.reflect.GeneratedMethodAccessor115.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java)
at java.lang.reflect.Method.invoke(Method.java)
at com.sun.enterprise.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1011)
...
at $Proxy92.onMessage(Unknown Source)
at com.sun.messaging.jms.ra.OnMessageRunner.run(OnMessageRunner.java)
at com.sun.enterprise.connectors.work.OneWork.doWork(OneWork.java:77)
at com.sun.corba.ee.impl.orbutil.threadpool.ThreadPoolImpl$WorkerThread.run(ThreadPoolImpl.java:555)
También en casos muy raros (5 veces durante una hora) obtenemos esta excepción:
java.sql.SQLException: I/O Error: Connection reset by peer: socket write error
at net.sourceforge.jtds.jdbc.TdsCore.executeSQL(TdsCore.java)
at net.sourceforge.jtds.jdbc.JtdsStatement.executeSQLQuery(JtdsStatement.java)
at net.sourceforge.jtds.jdbc.JtdsPreparedStatement.executeQuery(JtdsPreparedStatement.java)
at com.acme.myejbs.MyMDB.doQuery2(MyMDB.java:126)
at com.acme.myejbs.MyMDB.onMessage(MyMDB.java:614)
...
Caused by: java.net.SocketException: Connection reset by peer: socket write error
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java)
at java.net.SocketOutputStream.write(SocketOutputStream.java)
at java.io.DataOutputStream.write(DataOutputStream.java)
at net.sourceforge.jtds.jdbc.SharedSocket.sendNetPacket(SharedSocket.java)
at net.sourceforge.jtds.jdbc.RequestStream.putPacket(RequestStream.java)
at net.sourceforge.jtds.jdbc.RequestStream.flush(RequestStream.java)
... 44 more
En casos raros obtenemos esta excepción miedo (NPE dentro jTDS):
java.lang.NullPointerException
at net.sourceforge.jtds.jdbc.JtdsPreparedStatement.executeQuery(JtdsPreparedStatement.java)
at com.acme.myejbs.MyMDB.doQuery2(MyMDB.java:126)
at com.acme.myejbs.MyMDB.onMessage(MyMDB.java:614)
...
no podemos encontrar por qué sucede esto. Las conexiones utilizadas nunca permanecen inactivas durante más de un segundo durante una solicitud. No sabemos quién está abandonando la conexión. Puede ser inestabilidad de la red, pero supongo que jTDS solo debe producir excepciones relacionadas con la red, ¿verdad?
Otra opción es alguna política o configuración del grupo de conexiones de Glassfish (tal vez Glassfish está cerrando conexiones físicas prematuramente), pero ¿cómo podemos rastrearlo?
Finalmente, MS SQL Server 2008 puede estar desconectando conexiones de forma remota, pero ¿cómo podemos controlar el lado del servidor para saber si está sucediendo?
cuando se utiliza el controlador JDBC de Microsoft de? – extraneon
@extraneon No probamos el controlador Microsoft JDBC. Existe una capa de persistencia completa basada en jTDS y el cambio a MS llevaría algunos días. – fernacolo
¿Su código captura/ignora las SQLExcepciones? ¿Estás usando un DataSource básico o un grupo de conexión? –