The second message -- connection has already been closed -- most likely arises 
from my code structure, which is:

Connection connection = null;
PreparedStatement prepStmt = null;
try
{
   Connection connection = getConnection();
   PreparedStatement prepStmt = connection.prepareStatement(...);
   ...
}
finally
{
   if (prepStmt != null) prepStmt.close();
   if (connection != null) connection.close();
}

The call to getConnection() in the try block gets a connection from the pool.  
But the server has closed this connection.  Therefore the call to 
prepareStatement throws a SQLException which is the last packet exception.

And we then enter the finally block, which tries to close the connection, and 
we get a new SQLException that the connection has already been closed.

In some places in my code I solve the issue of throwing a new exception by a 
new local variable as follows:

SQLException caught = null;
Connection connection = null;
PreparedStatement prepStmt = null;
try
{
   Connection connection = getConnection();
   PreparedStatement prepStmt = connection.prepareStatement(...);
   ...
}
catch (SQLException e)
{
   caught = e;
   throw e;
}
finally
{
   if (prepStmt != null)
   {
      try
      {
         prepStmt.close();
      }
      catch (SQLException e)
      {
         if (caught == null) caught = e;
      }
   }
   if (connection != null)
   {
      try
      {
         connection.close();
      }
      catch (SQLException e)
      {
         if (caught == null) caught = e;
      }
   }
   if (caught != null) throw caught;
}

But it's just too much code to write!

Probably with the validationQuey I won't have to worry about the last packet 
exception.  Let me try it out.

I removed minEvictableIdleTimeMillis and testWhileIdle.

Is there any doc about the ping in "/*ping*/ select 1"?

And yes, I got these errors even without my driver.


FYI, my driver class is

import com.mysql.jdbc.NonRegisteringDriver;

public class DecryptPasswordMysqlDriver extends NonRegisteringDriver
{
   public static final String urlPrefix = "mydriver@";
   private static final String usernamePropertyName = "user";
   private static final String passwordPropertyName = "password";

   static
   {
      try
      {
         DriverManager.registerDriver(new DecryptPasswordMysqlDriver());
      }
      catch (SQLException e)
      {
         throw new RuntimeException("Can't register " + 
DecryptPasswordMysqlDriver.class.getName());
      }
   }
   
   public DecryptPasswordMysqlDriver() throws SQLException
   {
      // Required for Class.forName().newInstance()
   }
   

   public boolean acceptsURL(String string) throws SQLException
   {
      if (string.startsWith(urlPrefix))
      {
         return super.acceptsURL(string.substring(urlPrefix.length()));
      }
      return false;
   }


   public Connection connect(String string, Properties properties) throws 
SQLException
   {
      if (!acceptsURL(string)) return null;
      // when the connection is created,
      //   the username and password come from context.xml and are therefore 
encoded
      // when the connection is borrowed from the pool,
      //   the username and password come from values saved by the pool, which 
are the decoded values
      String encodedPassword = (String) properties.get(passwordPropertyName);
      if (MapUtils.canDecrypt(encodedPassword))
      {
         String username = (String) properties.get(usernamePropertyName);
         String password = MapUtils.decrypt(username, encodedPassword);
         username = baseUsername + '_' + username;
         properties.put(usernamePropertyName, username);
         properties.put(passwordPropertyName, password);
      }
      Connection connection = 
super.connect(string.substring(urlPrefix.length()), properties);
      return connection;
   }
}




--- On Fri, 12/5/08, Christopher Schultz <[EMAIL PROTECTED]> wrote:

> From: Christopher Schultz <[EMAIL PROTECTED]>
> Subject: Re: configure JNDI to avoid error "Last packet sent to the server 
> was xxxxx ms ago"
> To: "Tomcat Users List" <users@tomcat.apache.org>
> Date: Friday, December 5, 2008, 9:20 AM
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> To whom it may concern,
> 
> [EMAIL PROTECTED] wrote:
> > How to configure JNDI in order to avoid the error
> "Last packet sent 
> > to the server was xxxxx ms ago."? I've seen
> two other errors which
> > seem to be from the same cause: "Communications
> link failure" and 
> > "Connection.close() has already been
> called".
> 
> I think the first one is a MySQL error message, so properly
> configuring
> your DataSource is key. The second one looks like you are
> being sloppy
> with your connections and closing them multiple times.
> 
> > The my.custom.mysql.Driver class decodes and decrypts
> the password,
> > and passes these to the base class, which is
> > com.mysql.jdbc.NonRegisteringDriver.
> 
> Aah, the old "why can't I encrypt my password in
> my <Resource>"
> question. Do you have any of these problems if you just use
> the standard
> MySQL driver (com.mysql.jdbc.Driver) directly? Just trying
> to rule-out
> problems caused by your delegating driver. Could you post
> the entire
> code of your Driver class?
> 
> > I read that the validationQuery="select 1"
> validates the connection
> > before returning it to the user.  If the select
> statement fails, then
> > the connection is removed from the pool and a new one
> is created.
> > 
> > What I'm not sure is if I need the
> testOnBorrow="true" and
> > testWhileIdle="true".
> 
> The default value for testOnBorrow is "true", so
> if you set a
> validationQuery, it will be used. I wouldn't bother
> setting
> testWhileIdle to "true".
> 
> > The MySQL connection timeout is 12 hours, so I set
> > minEvictableIdleTimeMillis="43200000".
> 
> That's a looong time. I suppose it doesn't matter
> much, but why set the
> timeout for so long?
> 
> > In my case, the last packet was sent about 57xxx
> milliseconds ago,
> > which is less than a minute, which is much less than
> the 12 hour
> > MySQL connection timeout.  Yet the error happens only
> after around 12
> > hours.  I expect the error to be "... 43200000 ms
> ago" or something
> > like that.
> 
> Yeah, I would expect that, too.
> 
> > Also, I read that the validationQuery adds performance
> overhead
> 
> Meh. It's a small price to pay to get known-good
> connections to the
> database. Try a query like "/* ping */ SELECT 1".
> In recent versions of
> the driver, this is the lightest-weight connection test you
> can perform,
> since it won't actually issue a query: it just checks
> the connection
> status to make sure you're good. If you have an older
> version of the
> driver, the "ping" is ignored and you'll
> execute a "SELECT 1" query
> which is pretty fast.
> 
> > and maybe the Java code should handle exceptions.
> 
> I'm not sure what you mean, here.
> 
> > But how would this
> > work?  I'm guessing you catch the SQLException,
> and if it is "Last
> > packet sent to the server was xxxxx ms ago" then
> call
> > connection.reconnect and attempt the operation again. 
> But
> > java.sql.Connection does not have a function
> reconnect.
> 
> Right: you'd need to close that connection (ignoring
> any errors) and get
> a new connection from the pool. I wouldn't recommend
> this. Instead, use
> validationQuery and let the pool do the checking for you.
> 
> > The other thing I could try in code, is that after
> getting the
> > connection by calling
> ctx.lookup("java:comp/env/jdbc/myname"), is to
> > call connection.isValid() or connection.isClosed(),
> and if the
> > connection is closed then call something to remove it
> from the pool.
> > But java.sql.Connection does not have a function
> > removeMeFromThePoolIBelongTo().
> 
> All of this stuff should be taken care of by the pool
> manager, so forget
> about it.
> 
> > Finally, I'm concerned about
> maxIdle="15" and removeAbandoned="true".
> > If a connection is used for a SQL statement then
> closed, it becomes
> > idle.  But then, will it be removed?  Or does
> removeAbandoned="true"
> > only apply to connections that are still active (i.e.
> > connection.close() has not been called on it) for
> > removeAbandonedTimeout="30" seconds?
> 
> An "abandoned" connection is one that has been
> checked out of the pool,
> but hadn't been returned by the time the
> removeAbandonedTimeout has
> expired. So, if your code checks-out a connection and then
> never returns
> it, that connection will eventually be removed from the
> pool (and
> replaced with a new one). I would recommend enabling
> "logAbandoned" so
> you'll get error messages when a connection is not
> returned to the pool.
> You'll get a stack trace of where the connection was
> requested, so it
> helps you track-down the place where you have a connection
> leak.
> 
> As for your "too many closes" problem, I suspect
> it is one of two problems:
> 
> 1. Your custom driver is improperly delegating to
> MySQL's driver
> 
> 2. Your application code is sloppy
> 
> I'll give you the advice I give everyone having JDBC
> connection
> problems: make sure all your code looks like the following.
> Check /all/
> your database accesses. Even one sloppy piece of code can
> leak
> connections or cause a pileup of resources on your database
> server.
> 
> Connection conn = null;
> PreparedStatement ps = null;
> ResultSet rs = null;
> 
> try
> {
>    conn = ... (get your connection)
> 
>    /*
>      ... do your query stuff.
> 
>      Do not close the connection, here!!
> 
>      If you close your ResultSet or Statement objects,
>      set them to null, too.
>    */
> }
> finally
> {
>   if(null != rs)
>        try { rs.close(); }
>        catch (SQLException sqle) { /* log me! */ }
> 
>   if(null != ps)
>        try { ps.close(); }
>        catch (SQLException sqle) { /* log me! */ }
> 
>   if(null != conn)
>        try { conn.close(); }
>        catch (SQLException sqle) { /* log me! */ }
> }
> 
> If you are using autocommit=false, things get more
> complicated. Let me
> know if you need the Full Monty.
> 
> - -chris
> 
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.9 (MingW32)
> Comment: Using GnuPG with Mozilla -
> http://enigmail.mozdev.org
> 
> iEYEARECAAYFAkk5Yt4ACgkQ9CaO5/Lv0PB2lgCgt5tEVfaa3IimE90CqrUXkGPg
> CmQAoJUXABF2vI5WQhgPHifY3fSyX7rE
> =Ld95
> -----END PGP SIGNATURE-----
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [EMAIL PROTECTED]
> For additional commands, e-mail:
> [EMAIL PROTECTED]

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to