summaryrefslogtreecommitdiffstats
path: root/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/Database.java
blob: 204cfcb0e4235a04ff7b026b696e3857e3593d14 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package org.openslx.bwlp.sat.database;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.openslx.bwlp.sat.util.Configuration;

public class Database {

	private static final Logger LOGGER = LogManager.getLogger(Database.class);
	/**
	 * Pool of available connections.
	 */
	private static final Queue<MysqlConnection> pool = new ConcurrentLinkedQueue<>();

	/**
	 * Set of connections currently handed out.
	 */
	private static final Set<MysqlConnection> busyConnections = Collections.newSetFromMap(new ConcurrentHashMap<MysqlConnection, Boolean>());

	static {
		try {
			// Hack for some Java versions to register and instantiate the MySQL connection driver
			Class.forName("org.mariadb.jdbc.Driver").newInstance();
		} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
			LOGGER.fatal("Cannot get mysql JDBC driver!", e);
			System.exit(1);
		}
	}

	/**
	 * Get a connection to the database. If there is a valid connection in the
	 * pool, it will be returned. Otherwise, a new connection is created. If
	 * there are more than 20 busy connections, <code>null</code> is returned.
	 * 
	 * @return connection to database, or <code>null</code>
	 */
	public static MysqlConnection getConnection() {
		MysqlConnection con;
		for (;;) {
			con = pool.poll();
			if (con == null)
				break;
			if (!con.isValid()) {
				con.release();
				continue;
			}
			if (!busyConnections.add(con))
				throw new RuntimeException("Tried to hand out a busy connection!");
			try {
				// By convention in our program we don't want auto commit
				con.setAutoCommit(false);
				return con;
			} catch (SQLException e) {
				con.release();
				continue;
			}
		}
		// No pooled connection
		if (busyConnections.size() > 20) {
			LOGGER.warn("Too many open MySQL connections. Possible connection leak!");
			return null;
		}
		try {
			// Create fresh connection
			Connection rawConnection = DriverManager.getConnection(Configuration.getDbUri() + "&permitMysqlScheme",
					Configuration.getDbUsername(), Configuration.getDbPassword());
			// By convention in our program we don't want auto commit
			rawConnection.setAutoCommit(false);
			// Wrap into our proxy
			con = new MysqlConnection(rawConnection);
			// Keep track of busy mysql connection
			if (!busyConnections.add(con))
				throw new RuntimeException("Tried to hand out a busy connection!");
			return con;
		} catch (SQLException e) {
			LOGGER.info("Failed to connect to local mysql server", e);
		}
		return null;
	}

	/**
	 * Called by a {@link MysqlConnection} when its <code>close()</code>-method
	 * is called, so the connection will be added to the pool of available
	 * connections again.
	 * 
	 * @param connection
	 */
	static void returnConnection(MysqlConnection connection) {
		if (!busyConnections.remove(connection))
			throw new RuntimeException("Tried to return a mysql connection to the pool that was not taken!");
		pool.add(connection);
	}

	public static void printCharsetInformation() {
		LOGGER.info("MySQL charset related variables:");
		try (MysqlConnection connection = Database.getConnection()) {
			MysqlStatement stmt = connection.prepareStatement("SHOW VARIABLES LIKE :what");
			stmt.setString("what", "char%");
			ResultSet rs = stmt.executeQuery();
			while (rs.next()) {
				LOGGER.info(rs.getString("Variable_name") + ": " + rs.getString("Value"));
			}
			stmt.setString("what", "collat%");
			rs = stmt.executeQuery();
			while (rs.next()) {
				LOGGER.info(rs.getString("Variable_name") + ": " + rs.getString("Value"));
			}
		} catch (SQLException e) {
			LOGGER.error("Query failed in Database.printCharsetInformation()", e);
		}
		LOGGER.info("End of variables");
	}

	public static void printDebug() {
		LOGGER.info("Available: " + pool.size());
		LOGGER.info("Busy: " + busyConnections.size());
	}

}// end class