# Webapp und mysql. mysql verliert die verbindung nach langer zeit



## sence (26. Jul 2009)

Guten Tag,

Ich benutze einen Connectionpool für die MYSQL verbindung meiner Webseite.

Jedoch verliert der Server bei langem nicht benutzen die komplette verbindung zu mysql, und ich muss das Projekt neu deployen, damit sie wieder initalisiert wird.

Hier der Connection pool selbst erstmal:

```
package database;

import java.sql.*;
import java.util.*;

/**
 * Diese Klasse reserviert, recyclet und regelt die 
 * Datenbankverbindungen über das Speichern der 
 * verschiedenen Connects in Vectoren
 */

public class ConnectionPool implements Runnable 
{
	/** Enthält den Datenbanktreiber */
	private String driver;
	
	/** Enthält die Datenbankurl */
	private String url;
	
	/** Enthält dem Usernamen für die Datenbank */
	private String username;
	
	/** Enthält das Passwort für die Datenbank */
	private String password;
	
	/** Enthält die maximale Anzahl der DbConnections */
	private int maxConnections;
	
	/** Gibt an ob über einen Thread auf freie Connections gewartet
	 * werden soll, oder eine SQL Exception geworfen wird, wenn alle
	 * Connects belegt sind
	 */
	private boolean waitIfBusy;
	
	/** Enthält die verfügbaren Connections */
	private Vector availableConnections;
	
	/** Enthält die Verbindungen, die in Betrieb sind */
	private Vector busyConnections;
	
	/** 
	 * Gibt an, ob im Hintergrund Connects gebildet werden sollen
	 * oder nicht
	 */
	private boolean connectionPending = false;

	public ConnectionPool(String driver, String url,
						String username, String password,
						int initialConnections,
						int maxConnections,
						boolean waitIfBusy)
	  throws SQLException
	  {
		this.driver = driver;
		this.url = url;
		this.username = username;
		this.password = password;
		this.maxConnections = maxConnections;
		this.waitIfBusy = waitIfBusy;
		
		if (initialConnections > maxConnections) 
		{
	  		initialConnections = maxConnections;
		}
		
		availableConnections = new Vector(initialConnections);
		busyConnections = new Vector();
		for(int i=0; i<initialConnections; i++) 
		{
	  		availableConnections.addElement(makeNewConnection());
		}
  	}

	/**
	 * Wenn eine Connection aus der Liste geschlossen ist
	 * oder ueber einen Timeout beendet, wird die Connection aus
	 * der Liste der gerade benutzten Connection entfernt und 
	 * evtl. wartende Threads werden benachrichtigt, dass jetzt
	 * wieder eine Connection frei ist, falls das maximale Limit
	 * an Connections bereits erreicht wurde
	 * @return Connection Der eigentliche Connect zur Datenbank
	 * @throws SQLException
	 */
	public synchronized Connection getConnection()
	throws SQLException 
	{
		if (!availableConnections.isEmpty()) 
		{
	  		Connection existingConnection = (Connection)availableConnections.lastElement();
	  		int lastIndex = availableConnections.size() - 1;
	  		availableConnections.removeElementAt(lastIndex);
			
			if (existingConnection.isClosed()) 
			{
				notifyAll(); // Freed up a spot for anybody waiting
				return(getConnection());
	  		} else 
	  		  {
				busyConnections.addElement(existingConnection);
				return(existingConnection);
	  		}
		} else
		  {
		
		/*Hier gibt es drei moegliche Faelle:
		 * 1. Die Anzahl maximaler Connections ist erreicht
		 *  und es wird eine neue im Hintergrund eingerichtet,
		 * 
		 * 2.  Die Anzahl maximaler Connections ist erreicht und der waitifBusy
		 * Flag steht auf false. Die SQL Exception geworfen.
		 *  
		 * 3. Die Anzahl maximaler Connections ist erreicht, aber der waitifBusy
		 * flag steht auf true. Dann wird gewartet, bis eine Connection frei
		 * ist.
		 */
	  	if ((totalConnections() < maxConnections) &&
		!connectionPending) 
		{
			makeBackgroundConnection();
	  	} else if (!waitIfBusy)
	  	  {
			throw new SQLException("Connection limit reached");
	  	  }
	  	  
		try 
		{
			wait();
	  }catch(InterruptedException ie) {}
	  	return(getConnection());
	 }
  }
	
	/**
	 * Ein Connect zur Datenbank kann nur hergestllt werden
	 * wenn eine Verbindung frei ist. Deshalb wird hier
	 * ein Thread gestartet, der eine Verbindung im 
	 * Hintergrund herstellt und dann auf eine freie Verbindung
	 * bzw. eine Nachricht wartet, dass eine Verbindung 
	 * freigegeben wurde.
	 */
	private void makeBackgroundConnection() 
	{
		connectionPending = true;
		try
		{
			Thread connectThread = new Thread(this);
	  		connectThread.start();
		} catch(OutOfMemoryError oome) 
		{
			System.out.println("Fehler in database.ConnectionPool: " + oome.getMessage());
		}
  }

	public void run()
	{
		try
		{
			Connection connection = makeNewConnection();
	  		synchronized(this)
	  		{
				availableConnections.addElement(connection);
				connectionPending = false;
				notifyAll();
	 	 }
		 } catch(Exception e) 
		 {
		 	System.out.println("Fehler in database.ConnectionPool run(): " + e.getMessage());
		 }
	}


	/**
 	* Stellt den eigentlichen Connect zur Datenbank her.
 	* @return Connection Die Datenbankverbindung
 	* @throws SQLException
 	*/
  	private Connection makeNewConnection()
	  throws SQLException 
	  {
	 	try 
	 	{
		// DbTreiber laden  
	  	Class.forName(driver);
	  
	  	// Netzwerkverbindung zur Datenbank herstellen
	  	Connection connection = DriverManager.getConnection(url, username, password);
	  	return(connection);
		}catch(ClassNotFoundException cnfe) {
	  	// Exception werfen, falls der Treiber nicht gefunden werden kann
	  	throw new SQLException("Can't find class for driver: " +
							 driver);
		}
  	}

	/**
	 * Hier werden Connections frei gegeben und aus dem busyConnections Vector
	 * rausgenommen, um beim Vector für freie Connects (availableConnections)
	 * eingefügt zu werden
	 * @param connection Die augenblickliche Connection
	 */
	public synchronized void free(Connection connection) 
	{
		busyConnections.removeElement(connection);
		availableConnections.addElement(connection);
		// Allen Threads Bescheid sagen, dass ein Connect frei geworden ist
		notifyAll();
  	}
	
	/**
	 * Gibt die Anzahl aller Connections zurück
	 * @return int Die Anzahl der Connection im Vector
	 */
	public synchronized int totalConnections()
	{
		return(availableConnections.size() +
		   busyConnections.size());
	}

  /**
   * Bietet die Moeglichkeit, alle Connections explizit zu schliessen.
   * Dies Funktion ist aber mit Vorsicht zu geniessen, wenn noch 
   * Connects zur Datenbank bestehen. Auserdem werden die ja 
   * automatisch geschlossen, wenn der GC laeuft. 
   * Die Funktion bietet jedoch die Moeglichkeit, einer groesseren 
   * Kontrolle.
   */
  public synchronized void closeAllConnections() 
  {
	closeConnections(availableConnections);
	availableConnections = new Vector();
	closeConnections(busyConnections);
	busyConnections = new Vector();
  }

  /**
   * Wird von closeAllConnections() automatisch aufgerufen
   * und ist deshalb privat
   * @param connections Die Anzahl der Verbindungen
   */
  private void closeConnections(Vector connections) 
  {
	try {
	  for(int i=0; i<connections.size(); i++) {
		Connection connection =
		  (Connection)connections.elementAt(i);
		if (!connection.isClosed()) 
		{
		  connection.close();
		}
	  }
	} catch(SQLException sqle) 
	{
	 	System.out.println("Fehler in database.ConnectionPool: " + sqle.getMessage());
	}
  }

  /**
   * Debug Ausgabe für alle Objektparameter
   * @see java.lang.Object#toString()
   */
  public synchronized String toString() {
	String info =
	  "ConnectionPool(" + url + "," + username + ")" +
	  ", available=" + availableConnections.size() +
	  ", busy=" + busyConnections.size() +
	  ", max=" + maxConnections;
	return(info);
  }
}
```


So dann der Initialisierer


```
package login;

import java.io.*;
import java.sql.*;

import javax.servlet.*;
import javax.servlet.http.*;

import database.*;


public class LoginServ extends HttpServlet
{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1303275261224006323L;


	private String dbDriver;
	
	private String dbUrl;
	
	private String dbUser;
	
	private String dbPass;
	
	/**
	 * Für die Instanziierung von  database.ConnectionPool()
	 */
	public static ConnectionPool CON;
	
	/**
	 * Der eigentliche Datenbankconnect
	 */
	private Connection connect;

/**
 * Die Servletinitialisierung. Hier werden alle notwendigen Parameter
 * für den Datenbankconnect geladen und der ConnectionPool wird instanziiert.
 * @see javax.servlet.Servlet#init(ServletConfig)
 */
	public void init() throws ServletException
	{
		// Init Parameter holen
		dbDriver = "com.mysql.jdbc.Driver";
		dbUrl = "jdbc:mysql://sql-cluster01.it-schuth.net:3306/meineDB";
		dbUser = "deruser";
		dbPass = "meinpass";
		try
		{
			// ConnectionPool Objekt anlegen
			CON = new ConnectionPool(dbDriver,dbUrl,dbUser,dbPass,5,20,true);
			
			// Den eigentlichen Connect herstellen
			connect = CON.getConnection();
		}
		catch (SQLException e)
		{
			System.err.println("Fehler beim aufbauen des Pools: " + e);
		}
	}

/**
 * Die Post Methode des Servlets. Hier werden Username und Passwort ausgelesen,
 * damit eine Datenbankabfrage erfolgen kann. 
 * Die Überprüfung des Users erfolgt über den Aufruf der statischen Methode 
 * CheckUser.checkUser(Connection,String,String) und je nachdem, ob diese
 * Methode true oder false zurückgibt, wird auf die entsprechende Fehlerseite
 * umgeleitet oder auf die Anzeigeseite des DbInhaltes.
 * @see javax.servlet.http.HttpServlet#doPost(HttpServletRequest, HttpServletResponse)
 */
	public void doPost(HttpServletRequest request, HttpServletResponse response)
	{		
		String user = request.getParameter("username");
		String pass = request.getParameter("password");
		
		if(!CheckUser.checkUser(connect, user, pass, request))
		{
			try
			{
				response.sendRedirect("./error_login");
			}
			catch (IOException e)
			{
				System.out.println("IOException in LoginServ.class: " + e.getMessage());
			}
		}else
		 {
		 	// Das Session Objekt holen und ein Attribut auf 1
		 	// setzten, um es auf jeder Seite abzufragen
		 	try
			{
				// die Url auf die umgeleitet werden soll
				String uri = request.getContextPath() + "/kundenbereich/index.jsp";
				
				// SICHERHEITSLEG
				
				Integer valid = new Integer(geheimeNummer :) );
				// SICHERHEITSLEG
				
				HttpSession session = request.getSession(true);
				session.setAttribute("valid", valid);
				session.setAttribute("username", user);
				// autoLoad Functions
				beans.CheckValidUser.getFlag(connect, request, user);
				beans.CheckValidUser.setID(connect, request, user);
	
						
				// Weiterleitung
				String encodedUrl = response.encodeRedirectURL(uri);
				response.sendRedirect(encodedUrl);
			}
			catch (IOException e)
			{
				System.out.println("Fehler beim Aufruf von data.jsp: " + e.getMessage());
			}
		 }
	}	
}
```

Der erste mysql Aufruf kommt zustande wenn kunden sich in den internen Bereich einloggen möchten, dort wird der benutzername und das passwort mit der mysql datenbank abgeglichen.

Jedoch geht dieser connect schon verloren, und die anderen welche im internen dann darauf folgend ablaufen sind ja von dem ersten abhänig :/

dankeeeeeeeeeee


----------



## faulelotte (26. Jul 2009)

1) Warum benutzt du nicht einen fertigen Connectionpool wie Jakarta DBCP oder c3p0 ?

2) Mysql beendet Connections standardmäßig nach 28800 Sekunden Inaktivität einer Connection (siehe MySQL :: MySQL 5.1 Reference Manual :: 5.1.4 Server System Variables)
Der Connectionpool kann aber angewiesen werden ein SQL Statement an die Datenbank zu senden, um zu sehen ob die Connection schon verworfen wurde oder nicht. Sprich er sorgt dann selbstständig dafür das du in so einem Fall eine neue Connection bekommst.


----------



## mvitz (26. Jul 2009)

faulelotte hat gesagt.:


> 1) Warum benutzt du nicht einen fertigen Connectionpool wie Jakarta DBCP oder c3p0 ?



Ergänzung hierzu:

Ich nehme an, du deployst auf einem Tomcat, dann kannst du auch den Tomcat dazu benutzen für dich einen Connection Pool aufzusetzen.

Dafür muss in das META-INF Verzeichnis des WAR Files folgende Datei:

context.xml:
[XML]<?xml version="1.0" encoding="UTF-8"?>
<Context>
  <Resource auth="Container" type="javax.sql.DataSource"
    maxActive="100" maxIdle="30" maxWait="10000"
    name="jdbc/datasource" driverClassName="org.apache.derby.jdbc.ClientDriver"
    url="jdbc:derby://localhost:1527/my-database" 
    password="xxx"  username="xxx"
  />
</Context>[/XML]

in einem Servlet kommst du dann folgendermaßen an die DataSource:

```
...
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
...
```


----------



## sence (26. Jul 2009)

a) habe keinen fertigen genommen, da ich mir den aus einem Beispiel übernommen habe.
b) kannte ich die anderen noch nicht, da ich noch nicht als soviel mit mysql zu tuen hatte.

zu habi55)
ok, ich versuch das mal : )
dankeee


----------



## CNail187 (27. Jul 2009)

Hallo!

Zu habi55's Tip mit der context.xml hätte ich auch etwas zu ergänzen:

- man kann dem JDBC-URL noch einen autoreconnect-Parameter übergeben
-> url="jdbc:mysql://localhost:3306/mydb?autoReconnect=true" />

und dann gibts auch noch das Validieren der Verbindung durch den Tomcat
-> (auch wieder context.xml) validationQuery="SELECT 1"

siehe auch
Apache Tomcat 6.0 - JNDI Resources HOW-TO

Grüße,
CNail


----------



## mvitz (27. Jul 2009)

CNail187 hat gesagt.:


> Hallo!
> 
> Zu habi55's Tip mit der context.xml hätte ich auch etwas zu ergänzen:
> 
> ...



Danke, die kannte ich auch noch nicht 

Wobei, bist du dir sicher, dass ersteres nicht von der Datenbank abhängt?


----------



## CNail187 (28. Jul 2009)

> Wobei, bist du dir sicher, dass ersteres nicht von der Datenbank abhängt?



Ja, du hast natürlich recht: das mit dem autoreconnect ist ein MySQL-spezifisches Feature.


----------

