import java.util.Map.Entry;
import java.util.Properties;
import java.util.StringJoiner;
+import java.util.concurrent.LinkedBlockingDeque;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DatabaseConnection {
+ public static class Link implements AutoCloseable {
+
+ private DatabaseConnection target;
+
+ protected Link(DatabaseConnection target) {
+ this.target = target;
+ }
+
+ @Override
+ public void close() {
+ synchronized (DatabaseConnection.class) {
+ Link i = instances.get(Thread.currentThread());
+ if (i != this) {
+ throw new Error();
+ }
+ instances.remove(Thread.currentThread());
+ pool.add(target);
+ }
+ }
+
+ }
+
public static final int MAX_CACHED_INSTANCES = 3;
private static class StatementDescriptor {
}
- public PreparedStatement getTarget() {
+ public synchronized PreparedStatement getTarget() {
return target;
}
}
- public static final int CURRENT_SCHEMA_VERSION = 6;
+ public static final int CURRENT_SCHEMA_VERSION = 17;
public static final int CONNECTION_TIMEOUT = 24 * 60 * 60;
private HashMap<StatementDescriptor, PreparedStatement> statements = new HashMap<StatementDescriptor, PreparedStatement>();
- HashSet<PreparedStatement> underUse = new HashSet<>();
+ private HashSet<PreparedStatement> underUse = new HashSet<>();
private static Properties credentials;
lastAction = System.currentTimeMillis();
}
- private static DatabaseConnection instance;
+ private static HashMap<Thread, Link> instances = new HashMap<>();
- public static DatabaseConnection getInstance() {
- if (instance == null) {
- synchronized (DatabaseConnection.class) {
- if (instance == null) {
- instance = new DatabaseConnection();
- }
- }
+ private static LinkedBlockingDeque<DatabaseConnection> pool = new LinkedBlockingDeque<>();
+
+ private static int connCount = 0;
+
+ public static synchronized DatabaseConnection getInstance() {
+ Link l = instances.get(Thread.currentThread());
+ if (l == null) {
+ throw new Error("No database connection allocated");
}
- return instance;
+ return l.target;
+ }
+
+ public static synchronized boolean hasInstance() {
+ Link l = instances.get(Thread.currentThread());
+ return l != null;
}
public static boolean isInited() {
throw new Error("Re-initiaizing is forbidden.");
}
credentials = conf;
- int version = 0;
- try (GigiPreparedStatement gigiPreparedStatement = new GigiPreparedStatement("SELECT version FROM \"schemeVersion\" ORDER BY version DESC LIMIT 1;")) {
- GigiResultSet rs = gigiPreparedStatement.executeQuery();
- if (rs.next()) {
- version = rs.getInt(1);
+ try (Link i = newLink(false)) {
+ int version = 0;
+ try (GigiPreparedStatement gigiPreparedStatement = new GigiPreparedStatement("SELECT version FROM \"schemeVersion\" ORDER BY version DESC LIMIT 1;")) {
+ GigiResultSet rs = gigiPreparedStatement.executeQuery();
+ if (rs.next()) {
+ version = rs.getInt(1);
+ }
}
+ if (version == CURRENT_SCHEMA_VERSION) {
+ return; // Good to go
+ }
+ if (version > CURRENT_SCHEMA_VERSION) {
+ throw new Error("Invalid database version. Please fix this.");
+ }
+ upgrade(version);
+ } catch (InterruptedException e) {
+ throw new Error(e);
}
- if (version == CURRENT_SCHEMA_VERSION) {
- return; // Good to go
- }
- if (version > CURRENT_SCHEMA_VERSION) {
- throw new Error("Invalid database version. Please fix this.");
- }
- upgrade(version);
- }
-
- public void beginTransaction() throws SQLException {
- c.setAutoCommit(false);
}
private static void upgrade(int version) {
Statement s = getInstance().c.createStatement();
try {
while (version < CURRENT_SCHEMA_VERSION) {
- try (InputStream resourceAsStream = DatabaseConnection.class.getResourceAsStream("upgrade/from_" + version + ".sql")) {
- if (resourceAsStream == null) {
- throw new Error("Upgrade script from version " + version + " was not found.");
- }
- SQLFileManager.addFile(s, resourceAsStream, ImportType.PRODUCTION);
- }
+ addUpgradeScript(Integer.toString(version), s);
version++;
}
s.addBatch("UPDATE \"schemeVersion\" SET version='" + version + "'");
}
}
- public void commitTransaction() throws SQLException {
- c.commit();
- c.setAutoCommit(true);
- }
-
- public void quitTransaction() {
- try {
- if ( !c.getAutoCommit()) {
- c.rollback();
- c.setAutoCommit(true);
+ private static void addUpgradeScript(String version, Statement s) throws Error, IOException, SQLException {
+ try (InputStream resourceAsStream = DatabaseConnection.class.getResourceAsStream("upgrade/from_" + version + ".sql")) {
+ if (resourceAsStream == null) {
+ throw new Error("Upgrade script from version " + version + " was not found.");
}
- } catch (SQLException e) {
- e.printStackTrace();
+ SQLFileManager.addFile(s, resourceAsStream, ImportType.PRODUCTION);
}
}
return underUse.size();
}
- public void lockedStatements(PrintWriter writer) {
+ public synchronized void lockedStatements(PrintWriter writer) {
writer.println(underUse.size());
for (PreparedStatement ps : underUse) {
for (Entry<StatementDescriptor, PreparedStatement> e : statements.entrySet()) {
}
}
}
+
+ public static synchronized Link newLink(boolean readOnly) throws InterruptedException {
+ if (instances.get(Thread.currentThread()) != null) {
+ throw new Error("There is already a connection allocated for this thread.");
+ }
+ if (pool.isEmpty() && connCount < 5) {
+ pool.addLast(new DatabaseConnection());
+ connCount++;
+ }
+ DatabaseConnection conn = pool.takeFirst();
+ try {
+ conn.c.setReadOnly(readOnly);
+ } catch (SQLException e) {
+ throw new Error(e);
+ }
+ Link l = new Link(conn);
+ instances.put(Thread.currentThread(), l);
+ return l;
+ }
}