package net.md_5.bungee.reconnect;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;

public class SQLReconnectHandler extends AbstractReconnectManager
{

    private final Connection connection;

    public SQLReconnectHandler() throws ClassNotFoundException, SQLException
    {
        Class.forName( "org.sqlite.JDBC" );
        connection = DriverManager.getConnection( "jdbc:sqlite:bungee.sqlite" );

        try ( PreparedStatement ps = connection.prepareStatement(
                "CREATE TABLE IF NOT EXISTS players ("
                + "playerId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
                + "username TEXT NOT NULL UNIQUE COLLATE NOCASE,"
                + "seen INTEGER,"
                + "server TEXT"
                + ");" ) )
        {
            ps.executeUpdate();
        }
    }

    @Override
    protected synchronized ServerInfo getStoredServer(ProxiedPlayer player)
    {
        String server = null;
        try ( PreparedStatement ps = connection.prepareStatement( "SELECT server FROM players WHERE username = ?" ) )
        {
            ps.setString( 1, player.getName() );
            try ( ResultSet rs = ps.executeQuery() )
            {
                if ( rs.next() )
                {
                    server = rs.getString( 1 );
                } else
                {
                    try ( PreparedStatement playerUpdate = connection.prepareStatement( "INSERT INTO players( username ) VALUES( ? )" ) )
                    {
                        playerUpdate.setString( 1, player.getName() );
                        playerUpdate.executeUpdate();
                    }
                }
            }
        } catch ( SQLException ex )
        {
            ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not load location for player " + player.getName(), ex );
        }

        return ProxyServer.getInstance().getServerInfo( server );
    }

    @Override
    public synchronized void setServer(ProxiedPlayer player)
    {

        try ( PreparedStatement ps = connection.prepareStatement( "UPDATE players SET server = ?, seen = ? WHERE username = ?" ) )
        {
            ps.setString( 1, player.getServer().getInfo().getName() );
            ps.setInt( 2, (int) ( System.currentTimeMillis() / 1000L ) );
            ps.setString( 3, player.getName() );
            ps.executeUpdate();
        } catch ( SQLException ex )
        {
            ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not save location for player " + player.getName() + " on server " + player.getServer().getInfo().getName(), ex );
        }
    }

    @Override
    public void save()
    {
    }

    @Override
    public synchronized void close()
    {
        try
        {
            connection.close();
        } catch ( SQLException ex )
        {
            ProxyServer.getInstance().getLogger().log( Level.WARNING, "Error closing SQLite connection", ex );
        }
    }
}

@@ -29,16 +29,58 @@
 nnect;%0A%0A
+import com.google.common.base.Throwables;%0A
 import j
@@ -218,24 +218,72 @@
 LException;%0A
+import java.util.HashSet;%0Aimport java.util.Set;%0A
 import java.
@@ -529,16 +529,20 @@
 e final 
+Set%3C
 Connecti
@@ -543,18 +543,22 @@
 nnection
- c
+%3E allC
 onnectio
@@ -562,87 +562,117 @@
 tion
+s = new HashSet%3C%3E()
 ;%0A
-%0A
     p
-ublic SQLReconnectHandler() throws ClassNotFoundException, SQLExcep
+rivate final ThreadLocal%3CConnection%3E connectionPool = new ThreadLocal%3CConnec
 tion
+%3E()
 %0A   
@@ -686,43 +686,71 @@
     
-Class.forName( %22org.sqlite.JDBC%22 );
+@Override%0A        protected Connection initialValue()%0A        %7B
 %0A   
@@ -746,33 +746,37 @@
       %7B%0A        
-c
+    C
 onnection = Driv
@@ -773,65 +773,448 @@
 ion 
-= DriverManager.getConnection( %22jdbc:sqlite:bungee.sqlite
+con = null;%0A            try%0A            %7B%0A                con = DriverManager.getConnection( %22jdbc:sqlite:bungee.sqlite%22 );%0A            %7D catch ( SQLException ex )%0A            %7B%0A                Throwables.propagate( ex );%0A            %7D%0A%0A            allConnections.add( con );%0A            return con;%0A        %7D%0A    %7D;%0A%0A    public SQLReconnectHandler() throws ClassNotFoundException, SQLException%0A    %7B%0A        Class.forName( %22org.sqlite.JDBC
 %22 );
@@ -1254,32 +1254,42 @@
  ps = connection
+Pool.get()
 .prepareStatemen
@@ -1665,29 +1665,16 @@
 otected 
-synchronized 
 ServerIn
@@ -1789,32 +1789,42 @@
  ps = connection
+Pool.get()
 .prepareStatemen
@@ -2205,32 +2205,42 @@
 ate = connection
+Pool.get()
 .prepareStatemen
@@ -2778,37 +2778,24 @@
 %0A    public 
-synchronized 
 void setServ
@@ -2873,16 +2873,26 @@
 nnection
+Pool.get()
 .prepare
@@ -3536,21 +3536,8 @@
 lic 
-synchronized 
 void
@@ -3551,36 +3551,102 @@
 )%0A    %7B%0A        
-try%0A
+for ( Connection con : allConnections )%0A        %7B%0A            try%0A    
         %7B%0A      
@@ -3651,24 +3651,21 @@
         
-connecti
+    c
 on.close
@@ -3664,24 +3664,28 @@
 on.close();%0A
+    
         %7D ca
@@ -3708,34 +3708,42 @@
 on ex )%0A        
-%7B%0A
+    %7B%0A    
             Prox
@@ -3841,22 +3841,68 @@
  );%0A        
-%7D
+    %7D%0A        %7D%0A        allConnections.clear();
 %0A    %7D%0A%7D%0A

Rework SQLite again to use thread local connections - closes #492