java.lang.Exception: DEBUG -- CLOSE BY CLIENT STACK TRACE 的理解

[2013-12-06 11:06:21,715] [C3P0PooledConnectionPoolManager[identityToken->2tl0n98y1iwg7cbdzzq7a|719f1f]-HelperThread-#2] DEBUG – com.mchange.v2.c3p0.impl.NewPooledConnection@484c6b closed by a client.
java.lang.Exception: DEBUG — CLOSE BY CLIENT STACK TRACE
    at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:646)
    at com.mchange.v2.c3p0.impl.NewPooledConnection.closeMaybeCheckedOut(NewPooledConnection.java:259)
    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.destroyResource(C3P0PooledConnectionPool.java:619)
    at com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask.run(BasicResourcePool.java:1024)
    at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:648)
[2013-12-06 11:06:21,716] [C3P0PooledConnectionPoolManager[identityToken->2tl0n98y1iwg7cbdzzq7a|719f1f]-HelperThread-#2] DEBUG – Successfully destroyed PooledConnection: com.mchange.v2.c3p0.impl.NewPooledConnection@484c6b

经过分析源码,得到的结论是这个异常可以无视掉。
相关源码如下(c3p0版本:c3p0-0.9.2.1):
package com.mchange.v2.c3p0.impl;

import java.util.*;
import java.sql.*;
import javax.sql.*;
import com.mchange.v2.c3p0.*;
import com.mchange.v2.c3p0.stmt.*;
import com.mchange.v2.c3p0.util.*;
import com.mchange.v2.log.*;

import java.lang.reflect.Method;
import com.mchange.v2.lang.ObjectUtils;
import com.mchange.v2.sql.SqlUtils;

public final class NewPooledConnection extends AbstractC3P0PooledConnection{

    private final static MLogger logger = MLog.getLogger( NewPooledConnection.class );
//  methods below must be called from sync'ed methods

    /*
     *  If a throwable cause is provided, the PooledConnection is known to be broken (cause is an invalidating exception)
     *  and this method will not throw any exceptions, even if some resource closes fail.
     *
     *  
     *  if resources unexpectedlay fail to close.
     */
    private void close( Throwable cause ) throws SQLException
    { close( cause, false ); }

    private void close( Throwable cause, boolean forced ) throws SQLException
    {
    assert Thread.holdsLock( this );

        if ( this.invalidatingException == null )
        {
            List closeExceptions = new LinkedList();

            // cleanup ResultSets
            cleanupResultSets( closeExceptions );

            // cleanup uncached Statements
        // System.err.println(this + ".close( ... ) -- uncachedActiveStatements: " + uncachedActiveStatements);
            cleanupUncachedStatements( closeExceptions );

            // cleanup cached Statements
            try
            { closeAllCachedStatements(); }
            catch ( SQLException e )
            { closeExceptions.add(e); }

        if ( forced )
        {
            // reset transaction state
            try { C3P0ImplUtils.resetTxnState( physicalConnection, forceIgnoreUnresolvedTransactions, autoCommitOnClose, false ); }
            catch (Exception e)
            {
                if (logger.isLoggable( MLevel.FINER ))
                logger.log( MLevel.FINER, 
                        "Failed to reset the transaction state of  " + physicalConnection + "just prior to close(). " +
                        "Only relevant at all if this was a Connection being forced close()ed midtransaction.", 
                        e );
            }
        }

            // cleanup physicalConnection
            try
            { physicalConnection.close(); }
            catch ( SQLException e )
            {
                if (logger.isLoggable( MLevel.FINER ))
                    logger.log( MLevel.FINER, "Failed to close physical Connection: " + physicalConnection, e );

                closeExceptions.add(e); 
            }

            // update our state to bad status and closed, and log any exceptions
            if ( connection_status == ConnectionTester.CONNECTION_IS_OKAY )
                connection_status = ConnectionTester.CONNECTION_IS_INVALID;
            if ( cause == null )
            {
                this.invalidatingException = NORMAL_CLOSE_PLACEHOLDER;

                if ( Debug.DEBUG && logger.isLoggable( MLevel.FINEST ) )
                    num:646 in the NewPooledConnection.java
/*                

Note that:
1、This is not an exception, the new Exception is used merely to show execution path for debug purposes.
2、And yes, this is only a debug message (actually, FINEST is the lowest possible level in java.util.logging).
To wrap this up: ignore and tune your logging levels to skip these.

http://stackoverflow.com/questions/8403227/weird-error-close-by-client-stack-trace

既然成功了,干嘛还要丢异常出来?

这里就不得不说到两个商业开发的原则问题了。
第一,对上家传入数据严加过滤,对传出给下家的数据仔细检查。
第二,合理使用异常。
第一点其实很简单的。也就是模块化开发的一个思想问题。对自己的行为负责。前端返回的数据究竟是什么,需要进行校验。不合格的剔除或者是修正。合格的处理完后,在传出之前也要加以校验,是否合格

结合到 “合理使用异常” 这句话来说呢,就是说,需要抛出异常的时候,就抛出。不需要抛出的时候,就不抛出。对程序员来说,在必要的时候看到一串异常信息,是最合适的事情了。

http://www.zhixing123.cn/jsp/23305.html

 

*/

                 logCloseExceptions( null, closeExceptions );

                if (closeExceptions.size() > 0)
                    throw new SQLException("Some resources failed to close properly while closing " + this);
            }
            else
            {
                this.invalidatingException = cause;
                if (Debug.TRACE >= Debug.TRACE_MED)
                    logCloseExceptions( cause, closeExceptions );
                else
                    logCloseExceptions( cause, null );
            }
        }
    }
 
/********************************************************************
 * This class generated by com.mchange.v2.debug.DebugGen
 * and will probably be overwritten by the same! Edit at
 * YOUR PERIL!!! Hahahahaha.
 ********************************************************************/

package com.mchange.v2.c3p0.impl;

import com.mchange.v2.debug.DebugConstants;

final class Debug implements DebugConstants
{
    
    final static int     TRACE = TRACE_MAX;

    private Debug()
    {}
}
 
package com.mchange.v2.log;

import java.util.*;

public final class MLevel
{
    public final static MLevel ALL;
    public final static MLevel CONFIG;
    public final static MLevel FINE;
    public final static MLevel FINER;
    public final static MLevel FINEST;
    public final static MLevel INFO;
    public final static MLevel OFF;
    public final static MLevel SEVERE;
    public final static MLevel WARNING;

    private final static Map integersToMLevels;
    private final static Map namesToMLevels;

    public static MLevel fromIntValue(int intval)
    { return (MLevel) integersToMLevels.get( new Integer( intval ) ); }

    public static MLevel fromSeverity(String name)
    { return (MLevel) namesToMLevels.get( name ); }

    static
    {
    Class lvlClass;
    boolean jdk14api;  //not just jdk14 -- it is possible for the api to be present with older vms
    try
        { 
        lvlClass = Class.forName( "java.util.logging.Level" ); 
        jdk14api = true;
        }
    catch (ClassNotFoundException e )
        { 
        lvlClass = null;
        jdk14api = false; 
        }

    MLevel all;
    MLevel config;
    MLevel fine;
    MLevel finer;
    MLevel finest;
    MLevel info;
    MLevel off;
    MLevel severe;
    MLevel warning;

    try
        { 
        // numeric values match the intvalues from java.util.logging.Level
        all = new MLevel( (jdk14api ? lvlClass.getField("ALL").get(null) : null), Integer.MIN_VALUE, "ALL" );
        config = new MLevel( (jdk14api ? lvlClass.getField("CONFIG").get(null) : null), 700, "CONFIG" );
        fine = new MLevel( (jdk14api ? lvlClass.getField("FINE").get(null) : null), 500, "FINE" );
        finer = new MLevel( (jdk14api ? lvlClass.getField("FINER").get(null) : null), 400, "FINER" );
        finest = new MLevel( (jdk14api ? lvlClass.getField("FINEST").get(null) : null), 300, "FINEST" );
        info = new MLevel( (jdk14api ? lvlClass.getField("INFO").get(null) : null), 800, "INFO" );
        off = new MLevel( (jdk14api ? lvlClass.getField("OFF").get(null) : null), Integer.MAX_VALUE, "OFF" );
        severe = new MLevel( (jdk14api ? lvlClass.getField("SEVERE").get(null) : null), 900, "SEVERE" );
        warning = new MLevel( (jdk14api ? lvlClass.getField("WARNING").get(null) : null), 1000, "WARNING" );
        }
    catch ( Exception e )
        { 
        e.printStackTrace();
        throw new InternalError("Huh? java.util.logging.Level is here, but not its expected public fields?");
        }

    ALL = all;
    CONFIG = config;
    FINE = fine;
    FINER = finer;
    FINEST = finest;
    INFO = info;
    OFF = off;
    SEVERE = severe;
    WARNING = warning;

    Map tmp = new HashMap();
    tmp.put( new Integer(all.intValue()), all);
    tmp.put( new Integer(config.intValue()), config);
    tmp.put( new Integer(fine.intValue()), fine);
    tmp.put( new Integer(finer.intValue()), finer);
    tmp.put( new Integer(finest.intValue()), finest);
    tmp.put( new Integer(info.intValue()), info);
    tmp.put( new Integer(off.intValue()), off);
    tmp.put( new Integer(severe.intValue()), severe);
    tmp.put( new Integer(warning.intValue()), warning);

    integersToMLevels = Collections.unmodifiableMap( tmp );

    tmp = new HashMap();
    tmp.put( all.getSeverity(), all);
    tmp.put( config.getSeverity(), config);
    tmp.put( fine.getSeverity(), fine);
    tmp.put( finer.getSeverity(), finer);
    tmp.put( finest.getSeverity(), finest);
    tmp.put( info.getSeverity(), info);
    tmp.put( off.getSeverity(), off);
    tmp.put( severe.getSeverity(), severe);
    tmp.put( warning.getSeverity(), warning);

    namesToMLevels = Collections.unmodifiableMap( tmp );
    }

    Object level;
    int    intval;
    String lvlstring;

    public int intValue()
    { return intval; }

    public Object asJdk14Level()
    { return level; }

    public String getSeverity()
    { return lvlstring; }

    public String toString()
    { return this.getClass().getName() + this.getLineHeader(); }

    public String getLineHeader()
    { return "[" + lvlstring + ']';}



    private MLevel(Object level, int intval, String lvlstring)
    {
    this.level = level;
    this.intval = intval;
    this.lvlstring = lvlstring;
    }
}
 

 

package java.util.logging;

import java.util.*;
import java.security.*;
import java.lang.ref.WeakReference;
 
private static final int offValue = Level.OFF.intValue();
    private volatile int levelValue;  // current effective level value
    /** * Check if a message of the given level would actually be logged
 * by this logger. This check is based on the Loggers effective level,
 * which may be inherited from its parent. *
 * @param level a message logging level
 * @return true if the given message level is currently being logged. */
点赞