# HG changeset patch
# User cli
# Date 1315671485 -7200
# Node ID 7e24949b87b0f8159b8efd2a509fed8ccc0ae1e4
# Parent  5d7d1adf387f39bbd3f489e80383614936630d4f
HSQLDB backend support completed, but untested.

diff -r 5d7d1adf387f -r 7e24949b87b0 README
--- a/README	Tue Jun 07 11:55:22 2011 +0200
+++ b/README	Sat Sep 10 18:18:05 2011 +0200
@@ -1,6 +1,9 @@
 sonews README
 =============
 
+sonews is an Usenet News Server written in Java. It uses a relation database as
+backend, currently supported is MySQL, PostgreSQL and HSQLDB.
+
 Prerequisites:
 --------------
 
@@ -23,5 +26,5 @@
 Bugs and other Issues:
 ----------------------
 
-Please mail them to christian.lins@fh-osnabrueck.de or better issue them
-into the bugtracker at http://bugs.xerxys.info/ .
\ No newline at end of file
+Please mail them to mail(at)sonews.org or better issue them
+into the bugtracker at https://bitbucket.org/cli/sonews/ .
diff -r 5d7d1adf387f -r 7e24949b87b0 helpers/database_hsqldb_tmpl.sql
--- a/helpers/database_hsqldb_tmpl.sql	Tue Jun 07 11:55:22 2011 +0200
+++ b/helpers/database_hsqldb_tmpl.sql	Sat Sep 10 18:18:05 2011 +0200
@@ -140,3 +140,4 @@
 );
 
 COMMIT;
+SHUTDOWN;
diff -r 5d7d1adf387f -r 7e24949b87b0 helpers/sonews.conf.sample
--- a/helpers/sonews.conf.sample	Tue Jun 07 11:55:22 2011 +0200
+++ b/helpers/sonews.conf.sample	Sat Sep 10 18:18:05 2011 +0200
@@ -2,3 +2,4 @@
 sonews.storage.user=SA
 sonews.storage.dbmsdriver=org.hsqldb.jdbcDriver
 sonews.storage.password=
+sonews.storage.provider=org.sonews.storage.impl.HSQLDBProvider
diff -r 5d7d1adf387f -r 7e24949b87b0 src/org/sonews/Main.java
--- a/src/org/sonews/Main.java	Tue Jun 07 11:55:22 2011 +0200
+++ b/src/org/sonews/Main.java	Sat Sep 10 18:18:05 2011 +0200
@@ -102,8 +102,9 @@
 		// Do NOT USE BackendConfig or Log classes before this point because they require
 		// a working JDBCDatabase connection.
 		try {
-			StorageProvider sprov =
-					StorageManager.loadProvider("org.sonews.storage.impl.JDBCDatabaseProvider");
+			String provName = Config.inst().get(Config.LEVEL_FILE,
+					Config.STORAGE_PROVIDER, "org.sonews.storage.impl.JDBCDatabaseProvider");
+			StorageProvider sprov = StorageManager.loadProvider(provName);
 			StorageManager.enableProvider(sprov);
 
 			// Make sure some elementary groups are existing
diff -r 5d7d1adf387f -r 7e24949b87b0 src/org/sonews/config/Config.java
--- a/src/org/sonews/config/Config.java	Tue Jun 07 11:55:22 2011 +0200
+++ b/src/org/sonews/config/Config.java	Sat Sep 10 18:18:05 2011 +0200
@@ -52,8 +52,7 @@
 	public static final String MLSEND_PORT = "sonews.mlsend.port";
 	public static final String MLSEND_USER = "sonews.mlsend.user";
 	/** Key constant. If value is "true" every I/O is written to logfile
-	 * (which is a lot!)
-	 */
+	 * (which is a lot!) */
 	public static final String DEBUG = "sonews.debug";
 	/** Key constant. Value is classname of the JDBC driver */
 	public static final String STORAGE_DBMSDRIVER = "sonews.storage.dbmsdriver";
@@ -63,6 +62,7 @@
 	public static final String STORAGE_USER = "sonews.storage.user";
 	/** Key constant. Value is the password for the DBMS. */
 	public static final String STORAGE_PASSWORD = "sonews.storage.password";
+	public static final String STORAGE_PROVIDER = "sonews.storage.provider";
 	/** Key constant. Value is the name of the host which is allowed to use the
 	 *  XDAEMON command; default: "localhost" */
 	public static final String XDAEMON_HOST = "sonews.xdaemon.host";
diff -r 5d7d1adf387f -r 7e24949b87b0 src/org/sonews/storage/StorageManager.java
--- a/src/org/sonews/storage/StorageManager.java	Tue Jun 07 11:55:22 2011 +0200
+++ b/src/org/sonews/storage/StorageManager.java	Sat Sep 10 18:18:05 2011 +0200
@@ -26,8 +26,7 @@
 
 	private static StorageProvider provider;
 
-	public static Storage current()
-			throws StorageBackendException {
+	public static Storage current() throws StorageBackendException {
 		synchronized (StorageManager.class) {
 			if (provider == null) {
 				return null;
@@ -41,7 +40,7 @@
 		try {
 			Class<?> clazz = Class.forName(pluginClassName);
 			Object inst = clazz.newInstance();
-			return (StorageProvider) inst;
+			return (StorageProvider)inst;
 		} catch (Exception ex) {
 			System.err.println(ex);
 			return null;
diff -r 5d7d1adf387f -r 7e24949b87b0 src/org/sonews/storage/impl/HSQLDB.java
--- a/src/org/sonews/storage/impl/HSQLDB.java	Tue Jun 07 11:55:22 2011 +0200
+++ b/src/org/sonews/storage/impl/HSQLDB.java	Sat Sep 10 18:18:05 2011 +0200
@@ -17,7 +17,10 @@
  */
 package org.sonews.storage.impl;
 
+import java.sql.SQLException;
+import org.sonews.storage.Channel;
 import org.sonews.storage.Storage;
+import org.sonews.storage.StorageBackendException;
 
 /**
  * A specialized JDBCDatabase supporting HSQLDB.
@@ -26,5 +29,31 @@
  */
 public class HSQLDB extends JDBCDatabase implements Storage {
 
+	@Override
+	protected void prepareAddGroupStatement() throws SQLException {
+		this.pstmtAddGroup0 = conn.prepareStatement(
+				"INSERT INTO groups (name, flags, group_id) VALUES (?, ?, IDENTITY())");
+	}
 
+	@Override
+	protected void prepareCountGroupsStatement() throws SQLException {
+		this.pstmtCountGroups = conn.prepareStatement(
+				"SELECT Count(group_id) FROM groups WHERE "
+				+ "BITAND(flags, " + Channel.DELETED + ") = 0");
+	}
+
+	@Override
+	protected void prepareGetPostingsCountStatement() throws SQLException {
+		this.pstmtGetPostingsCount = conn.prepareStatement(
+				"SELECT Count(*) FROM postings JOIN groups "
+				+ "ON groups.name = ? GROUP BY groups.name");
+	}
+
+	@Override
+	protected void prepareGetSubscriptionsStatement() throws SQLException {
+		this.pstmtGetSubscriptions = conn.prepareStatement(
+				"SELECT * FROM (SELECT feedtype, host, port, peer_id FROM peers JOIN "
+				+ "peer_subscriptions ON peers.peer_id = peer_subscriptions.peer_id) "
+				+ "JOIN groups ON group_id = groups.group_id WHERE feedtype = ?");
+	}
 }
diff -r 5d7d1adf387f -r 7e24949b87b0 src/org/sonews/storage/impl/HSQLDBProvider.java
--- a/src/org/sonews/storage/impl/HSQLDBProvider.java	Tue Jun 07 11:55:22 2011 +0200
+++ b/src/org/sonews/storage/impl/HSQLDBProvider.java	Sat Sep 10 18:18:05 2011 +0200
@@ -17,6 +17,9 @@
  */
 package org.sonews.storage.impl;
 
+import java.sql.SQLException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 import org.sonews.storage.Storage;
 import org.sonews.storage.StorageBackendException;
 import org.sonews.storage.StorageProvider;
@@ -27,13 +30,28 @@
  * @since sonews/1.1
  */
 public class HSQLDBProvider implements StorageProvider {
+	protected static final Map<Thread, HSQLDB> instances =
+			new ConcurrentHashMap<Thread, HSQLDB>();
 
+	@Override
 	public boolean isSupported(String uri) {
 		return uri.startsWith("jdbc:hsqldb");
 	}
 
+	@Override
 	public Storage storage(Thread thread) throws StorageBackendException {
-		throw new UnsupportedOperationException("Not supported yet.");
+		try {
+			if (!instances.containsKey(Thread.currentThread())) {
+				HSQLDB db = new HSQLDB();
+				db.arise();
+				instances.put(Thread.currentThread(), db);
+				return db;
+			} else {
+				return instances.get(Thread.currentThread());
+			}
+		} catch (SQLException ex) {
+			throw new StorageBackendException(ex);
+		}
 	}
 
 }
diff -r 5d7d1adf387f -r 7e24949b87b0 src/org/sonews/storage/impl/JDBCDatabase.java
--- a/src/org/sonews/storage/impl/JDBCDatabase.java	Tue Jun 07 11:55:22 2011 +0200
+++ b/src/org/sonews/storage/impl/JDBCDatabase.java	Sat Sep 10 18:18:05 2011 +0200
@@ -27,6 +27,7 @@
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.List;
+import java.util.logging.Level;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
@@ -97,6 +98,29 @@
 	/** How many times the database connection was reinitialized */
 	protected int restarts = 0;
 
+	protected void prepareAddGroupStatement() throws SQLException {
+		this.pstmtAddGroup0 = conn.prepareStatement(
+				"INSERT INTO groups (name, flags) VALUES (?, ?)");
+	}
+
+	protected void prepareCountGroupsStatement() throws SQLException {
+		this.pstmtCountGroups = conn.prepareStatement(
+				"SELECT Count(group_id) FROM groups WHERE "
+				+ "flags & " + Channel.DELETED + " = 0");
+	}
+
+	protected void prepareGetPostingsCountStatement() throws SQLException {
+		this.pstmtGetPostingsCount = conn.prepareStatement(
+				"SELECT Count(*) FROM postings NATURAL JOIN groups "
+				+ "WHERE groups.name = ?");
+	}
+
+	protected void prepareGetSubscriptionsStatement() throws SQLException {
+		this.pstmtGetSubscriptions = conn.prepareStatement(
+				"SELECT host, port, name FROM peers NATURAL JOIN "
+				+ "peer_subscriptions NATURAL JOIN groups WHERE feedtype = ?");
+	}
+
 	/**
 	 * Rises the database: reconnect and recreate all prepared statements.
 	 * @throws java.lang.SQLException
@@ -137,17 +161,14 @@
 				"INSERT INTO events VALUES (?, ?, ?)");
 
 			// Prepare statement for method addGroup()
-			this.pstmtAddGroup0 = conn.prepareStatement(
-				"INSERT INTO groups (name, flags) VALUES (?, ?)");
+			prepareAddGroupStatement();
 
 			// Prepare statement for method countArticles()
 			this.pstmtCountArticles = conn.prepareStatement(
 				"SELECT Count(article_id) FROM article_ids");
 
 			// Prepare statement for method countGroups()
-			this.pstmtCountGroups = conn.prepareStatement(
-				"SELECT Count(group_id) FROM groups WHERE "
-				+ "flags & " + Channel.DELETED + " = 0");
+			prepareCountGroupsStatement();
 
 			// Prepare statements for method delete(article)
 			this.pstmtDeleteArticle0 = conn.prepareStatement(
@@ -254,14 +275,10 @@
 				"SELECT Min(article_index) FROM postings WHERE group_id = ?");
 
 			// Prepare statement for method getPostingsCount()
-			this.pstmtGetPostingsCount = conn.prepareStatement(
-				"SELECT Count(*) FROM postings NATURAL JOIN groups "
-				+ "WHERE groups.name = ?");
+			prepareGetPostingsCountStatement();
 
 			// Prepare statement for method getSubscriptions()
-			this.pstmtGetSubscriptions = conn.prepareStatement(
-				"SELECT host, port, name FROM peers NATURAL JOIN "
-				+ "peer_subscriptions NATURAL JOIN groups WHERE feedtype = ?");
+			prepareGetSubscriptionsStatement();
 
 			// Prepare statement for method isArticleExisting()
 			this.pstmtIsArticleExisting = conn.prepareStatement(
@@ -1364,12 +1381,12 @@
 		}
 	}
 
-	private void restartConnection(SQLException cause)
+	protected void restartConnection(SQLException cause)
 		throws StorageBackendException
 	{
 		restarts++;
-		Log.get().severe(Thread.currentThread()
-			+ ": Database connection was closed (restart " + restarts + ").");
+		Log.get().log(Level.SEVERE, Thread.currentThread()
+			+ ": Database connection was closed (restart " + restarts + ").", cause);
 
 		if (restarts >= MAX_RESTARTS) {
 			// Delete the current, probably broken JDBCDatabase instance.
diff -r 5d7d1adf387f -r 7e24949b87b0 src/org/sonews/storage/impl/JDBCDatabaseProvider.java
--- a/src/org/sonews/storage/impl/JDBCDatabaseProvider.java	Tue Jun 07 11:55:22 2011 +0200
+++ b/src/org/sonews/storage/impl/JDBCDatabaseProvider.java	Sat Sep 10 18:18:05 2011 +0200
@@ -31,7 +31,8 @@
  */
 public class JDBCDatabaseProvider implements StorageProvider {
 
-	protected static final Map<Thread, JDBCDatabase> instances = new ConcurrentHashMap<Thread, JDBCDatabase>();
+	protected static final Map<Thread, JDBCDatabase> instances =
+			new ConcurrentHashMap<Thread, JDBCDatabase>();
 
 	@Override
 	public boolean isSupported(String uri) {