Question

Is there any way to check the connection leakage in a Java EE application?

The application is running on my local machine. It uses a MySQL database and a user enters his details into this database.

In my opinion connection leakage means not closing the connection object properly. I am creating too many database connections in my application. I want to check if there is any connection leakage in the database connections.

Was it helpful?

Solution

log4jdbc, a Java JDBC driver that can log SQL and/or JDBC calls for other JDBC drivers, has a logger which logs connection open and close events as well as dumping all open connection numbers. This is very useful for hunting down connection leak problems.

Another tool that you might want to check is ConnLeakFinder, a simple tool to pinpoint jdbc connection leaks in java code. I don't have any experience with it though.

OTHER TIPS

If you're using a Java EE app server, you should be able to configure it to check connections when they go out and reclaim stale connections when they don't come back.

Connection leakage is indeed a problem. I'd be worried if you had connection management scattered in so many places in the code that it was a big problem to find them all. I'd expect to see a Java EE connection pool that was used only within a well-defined persistence layer. Connections should be opened by a service layer that manages the transaction for that unit of work and closes it as soon as the use case is over, within method scope in a finally block.

If that's not true, I think it's time to refactor.

try using FindBug. It is a static code analysis tool and available as eclipse plugin as well as standalone application. Apart from Connection leackage it will find other problems in your application also

Use a connection factory, for example:

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

public class ConnectionFactory {
    private static Connection connection;

    public static synchronized Connection getConnection() throws SQLException {
        if (connection == null || connection.isClosed()) {
            connection = DriverManager.getConnection("url");
        }
        return connection;
    }
}

This way you never leave unattended connections behind. Use a connection pool if you need more than one connection (for performance). Most appservers have a JDBC connection pool facility.

The best way to tackle connection leaks is to do it during testing.

You can use an automated utility so that each test verifies if there is a connection leak.

@BeforeClass
public static void initConnectionLeakUtility() {
    if ( enableConnectionLeakDetection ) {
        connectionLeakUtil = new ConnectionLeakUtil();
    }
}

@AfterClass
public static void assertNoLeaks() {
    if ( enableConnectionLeakDetection ) {
        connectionLeakUtil.assertNoLeaks();
    }
}

The ConnectionLeakUtil looks like this:

public class ConnectionLeakUtil {

    private JdbcProperties jdbcProperties = JdbcProperties.INSTANCE;

    private List idleConnectionCounters = 
        Arrays.asList(
            H2IdleConnectionCounter.INSTANCE,
            OracleIdleConnectionCounter.INSTANCE,
            PostgreSQLIdleConnectionCounter.INSTANCE,
            MySQLIdleConnectionCounter.INSTANCE
    );

    private IdleConnectionCounter connectionCounter;

    private int connectionLeakCount;

    public ConnectionLeakUtil() {
        for ( IdleConnectionCounter connectionCounter : 
            idleConnectionCounters ) {
            if ( connectionCounter.appliesTo( 
                Dialect.getDialect().getClass() ) ) {
                this.connectionCounter = connectionCounter;
                break;
            }
        }
        if ( connectionCounter != null ) {
            connectionLeakCount = countConnectionLeaks();
        }
    }

    public void assertNoLeaks() {
        if ( connectionCounter != null ) {
            int currentConnectionLeakCount = countConnectionLeaks();
            int diff = currentConnectionLeakCount - connectionLeakCount;
            if ( diff > 0 ) {
                throw new ConnectionLeakException( 
                    String.format(
                        "%d connection(s) have been leaked! Previous leak count: %d, Current leak count: %d",
                        diff,
                        connectionLeakCount,
                        currentConnectionLeakCount
                    ) 
                );
            }
        }
    }

    private int countConnectionLeaks() {
        try ( Connection connection = newConnection() ) {
            return connectionCounter.count( connection );
        }
        catch ( SQLException e ) {
            throw new IllegalStateException( e );
        }
    }

    private Connection newConnection() {
        try {
            return DriverManager.getConnection(
                jdbcProperties.getUrl(),
                jdbcProperties.getUser(),
                jdbcProperties.getPassword()
            );
        }
        catch ( SQLException e ) {
            throw new IllegalStateException( e );
        }
    }
}

The IdleConnectionCounter implementations can be found in this blog post, and the MySQL version like this:

public class MySQLIdleConnectionCounter implements IdleConnectionCounter {

    public static final IdleConnectionCounter INSTANCE = 
        new MySQLIdleConnectionCounter();

    @Override
    public boolean appliesTo(Class<? extends Dialect> dialect) {
        return MySQL5Dialect.class.isAssignableFrom( dialect );
    }

    @Override
    public int count(Connection connection) {
        try ( Statement statement = connection.createStatement() ) {
            try ( ResultSet resultSet = statement.executeQuery(
                    "SHOW PROCESSLIST" ) ) {
                int count = 0;
                while ( resultSet.next() ) {
                    String state = resultSet.getString( "command" );
                    if ( "sleep".equalsIgnoreCase( state ) ) {
                        count++;
                    }
                }
                return count;
            }
        }
        catch ( SQLException e ) {
            throw new IllegalStateException( e );
        }
    }
}

Now, when you run your tests, you'll get a failure when a connection is being leaked.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top