src/org/sonews/storage/impl/JDBCDatabase.java
changeset 37 74139325d305
parent 35 ed84c8bdd87b
child 38 fdfc7225f799
     1.1 --- a/src/org/sonews/storage/impl/JDBCDatabase.java	Sun Aug 29 17:28:58 2010 +0200
     1.2 +++ b/src/org/sonews/storage/impl/JDBCDatabase.java	Sun Aug 29 18:17:37 2010 +0200
     1.3 @@ -52,1731 +52,1378 @@
     1.4  public class JDBCDatabase implements Storage
     1.5  {
     1.6  
     1.7 -  public static final int MAX_RESTARTS = 2;
     1.8 -  
     1.9 -  private Connection        conn = null;
    1.10 -  private PreparedStatement pstmtAddArticle1 = null;
    1.11 -  private PreparedStatement pstmtAddArticle2 = null;
    1.12 -  private PreparedStatement pstmtAddArticle3 = null;
    1.13 -  private PreparedStatement pstmtAddArticle4 = null;
    1.14 -  private PreparedStatement pstmtAddGroup0   = null;
    1.15 -  private PreparedStatement pstmtAddEvent = null;
    1.16 -  private PreparedStatement pstmtCountArticles = null;
    1.17 -  private PreparedStatement pstmtCountGroups   = null;
    1.18 -  private PreparedStatement pstmtDeleteArticle0 = null;
    1.19 -  private PreparedStatement pstmtDeleteArticle1 = null;
    1.20 -  private PreparedStatement pstmtDeleteArticle2 = null;
    1.21 -  private PreparedStatement pstmtDeleteArticle3 = null;
    1.22 -  private PreparedStatement pstmtGetArticle0 = null;
    1.23 -  private PreparedStatement pstmtGetArticle1 = null;
    1.24 -  private PreparedStatement pstmtGetArticleHeaders0 = null;
    1.25 -  private PreparedStatement pstmtGetArticleHeaders1 = null;
    1.26 -  private PreparedStatement pstmtGetArticleHeads = null;
    1.27 -  private PreparedStatement pstmtGetArticleIDs   = null;
    1.28 -  private PreparedStatement pstmtGetArticleIndex    = null;
    1.29 -  private PreparedStatement pstmtGetConfigValue = null;
    1.30 -  private PreparedStatement pstmtGetEventsCount0 = null;
    1.31 -  private PreparedStatement pstmtGetEventsCount1 = null;
    1.32 -  private PreparedStatement pstmtGetGroupForList = null;
    1.33 -  private PreparedStatement pstmtGetGroup0     = null;
    1.34 -  private PreparedStatement pstmtGetGroup1     = null;
    1.35 -  private PreparedStatement pstmtGetFirstArticleNumber = null;
    1.36 -  private PreparedStatement pstmtGetListForGroup       = null;
    1.37 -  private PreparedStatement pstmtGetLastArticleNumber  = null;
    1.38 -  private PreparedStatement pstmtGetMaxArticleID       = null;
    1.39 -  private PreparedStatement pstmtGetMaxArticleIndex    = null;
    1.40 -  private PreparedStatement pstmtGetOldestArticle      = null;
    1.41 -  private PreparedStatement pstmtGetPostingsCount      = null;
    1.42 -  private PreparedStatement pstmtGetSubscriptions  = null;
    1.43 -  private PreparedStatement pstmtIsArticleExisting = null;
    1.44 -  private PreparedStatement pstmtIsGroupExisting = null;
    1.45 -  private PreparedStatement pstmtPurgeGroup0     = null;
    1.46 -  private PreparedStatement pstmtPurgeGroup1     = null;
    1.47 -  private PreparedStatement pstmtSetConfigValue0 = null;
    1.48 -  private PreparedStatement pstmtSetConfigValue1 = null;
    1.49 -  private PreparedStatement pstmtUpdateGroup     = null;
    1.50 -  
    1.51 -  /** How many times the database connection was reinitialized */
    1.52 -  private int restarts = 0;
    1.53 -  
    1.54 -  /**
    1.55 -   * Rises the database: reconnect and recreate all prepared statements.
    1.56 -   * @throws java.lang.SQLException
    1.57 -   */
    1.58 -  protected void arise()
    1.59 -    throws SQLException
    1.60 -  {
    1.61 -    try
    1.62 -    {
    1.63 -      // Load database driver
    1.64 -      Class.forName(
    1.65 -        Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_DBMSDRIVER, "java.lang.Object"));
    1.66 +	public static final int MAX_RESTARTS = 2;
    1.67 +	private Connection conn = null;
    1.68 +	private PreparedStatement pstmtAddArticle1 = null;
    1.69 +	private PreparedStatement pstmtAddArticle2 = null;
    1.70 +	private PreparedStatement pstmtAddArticle3 = null;
    1.71 +	private PreparedStatement pstmtAddArticle4 = null;
    1.72 +	private PreparedStatement pstmtAddGroup0 = null;
    1.73 +	private PreparedStatement pstmtAddEvent = null;
    1.74 +	private PreparedStatement pstmtCountArticles = null;
    1.75 +	private PreparedStatement pstmtCountGroups = null;
    1.76 +	private PreparedStatement pstmtDeleteArticle0 = null;
    1.77 +	private PreparedStatement pstmtDeleteArticle1 = null;
    1.78 +	private PreparedStatement pstmtDeleteArticle2 = null;
    1.79 +	private PreparedStatement pstmtDeleteArticle3 = null;
    1.80 +	private PreparedStatement pstmtGetArticle0 = null;
    1.81 +	private PreparedStatement pstmtGetArticle1 = null;
    1.82 +	private PreparedStatement pstmtGetArticleHeaders0 = null;
    1.83 +	private PreparedStatement pstmtGetArticleHeaders1 = null;
    1.84 +	private PreparedStatement pstmtGetArticleHeads = null;
    1.85 +	private PreparedStatement pstmtGetArticleIDs = null;
    1.86 +	private PreparedStatement pstmtGetArticleIndex = null;
    1.87 +	private PreparedStatement pstmtGetConfigValue = null;
    1.88 +	private PreparedStatement pstmtGetEventsCount0 = null;
    1.89 +	private PreparedStatement pstmtGetEventsCount1 = null;
    1.90 +	private PreparedStatement pstmtGetGroupForList = null;
    1.91 +	private PreparedStatement pstmtGetGroup0 = null;
    1.92 +	private PreparedStatement pstmtGetGroup1 = null;
    1.93 +	private PreparedStatement pstmtGetFirstArticleNumber = null;
    1.94 +	private PreparedStatement pstmtGetListForGroup = null;
    1.95 +	private PreparedStatement pstmtGetLastArticleNumber = null;
    1.96 +	private PreparedStatement pstmtGetMaxArticleID = null;
    1.97 +	private PreparedStatement pstmtGetMaxArticleIndex = null;
    1.98 +	private PreparedStatement pstmtGetOldestArticle = null;
    1.99 +	private PreparedStatement pstmtGetPostingsCount = null;
   1.100 +	private PreparedStatement pstmtGetSubscriptions = null;
   1.101 +	private PreparedStatement pstmtIsArticleExisting = null;
   1.102 +	private PreparedStatement pstmtIsGroupExisting = null;
   1.103 +	private PreparedStatement pstmtPurgeGroup0 = null;
   1.104 +	private PreparedStatement pstmtPurgeGroup1 = null;
   1.105 +	private PreparedStatement pstmtSetConfigValue0 = null;
   1.106 +	private PreparedStatement pstmtSetConfigValue1 = null;
   1.107 +	private PreparedStatement pstmtUpdateGroup = null;
   1.108 +	/** How many times the database connection was reinitialized */
   1.109 +	private int restarts = 0;
   1.110  
   1.111 -      // Establish database connection
   1.112 -      this.conn = DriverManager.getConnection(
   1.113 -        Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_DATABASE, "<not specified>"),
   1.114 -        Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_USER, "root"),
   1.115 -        Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_PASSWORD, ""));
   1.116 +	/**
   1.117 +	 * Rises the database: reconnect and recreate all prepared statements.
   1.118 +	 * @throws java.lang.SQLException
   1.119 +	 */
   1.120 +	protected void arise()
   1.121 +		throws SQLException
   1.122 +	{
   1.123 +		try {
   1.124 +			// Load database driver
   1.125 +			Class.forName(
   1.126 +				Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_DBMSDRIVER, "java.lang.Object"));
   1.127  
   1.128 -      this.conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
   1.129 -      if(this.conn.getTransactionIsolation() != Connection.TRANSACTION_SERIALIZABLE)
   1.130 -      {
   1.131 -        Log.get().warning("Database is NOT fully serializable!");
   1.132 -      }
   1.133 +			// Establish database connection
   1.134 +			this.conn = DriverManager.getConnection(
   1.135 +				Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_DATABASE, "<not specified>"),
   1.136 +				Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_USER, "root"),
   1.137 +				Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_PASSWORD, ""));
   1.138  
   1.139 -      // Prepare statements for method addArticle()
   1.140 -      this.pstmtAddArticle1 = conn.prepareStatement(
   1.141 -        "INSERT INTO articles (article_id, body) VALUES(?, ?)");
   1.142 -      this.pstmtAddArticle2 = conn.prepareStatement(
   1.143 -        "INSERT INTO headers (article_id, header_key, header_value, header_index) " +
   1.144 -        "VALUES (?, ?, ?, ?)");
   1.145 -      this.pstmtAddArticle3 = conn.prepareStatement(
   1.146 -        "INSERT INTO postings (group_id, article_id, article_index)" +
   1.147 -        "VALUES (?, ?, ?)");
   1.148 -      this.pstmtAddArticle4 = conn.prepareStatement(
   1.149 -        "INSERT INTO article_ids (article_id, message_id) VALUES (?, ?)");
   1.150 +			this.conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
   1.151 +			if (this.conn.getTransactionIsolation() != Connection.TRANSACTION_SERIALIZABLE) {
   1.152 +				Log.get().warning("Database is NOT fully serializable!");
   1.153 +			}
   1.154  
   1.155 -      // Prepare statement for method addStatValue()
   1.156 -      this.pstmtAddEvent = conn.prepareStatement(
   1.157 -        "INSERT INTO events VALUES (?, ?, ?)");
   1.158 -     
   1.159 -      // Prepare statement for method addGroup()
   1.160 -      this.pstmtAddGroup0 = conn.prepareStatement(
   1.161 -        "INSERT INTO groups (name, flags) VALUES (?, ?)");
   1.162 -      
   1.163 -      // Prepare statement for method countArticles()
   1.164 -      this.pstmtCountArticles = conn.prepareStatement(
   1.165 -        "SELECT Count(article_id) FROM article_ids");
   1.166 -      
   1.167 -      // Prepare statement for method countGroups()
   1.168 -      this.pstmtCountGroups = conn.prepareStatement(
   1.169 -        "SELECT Count(group_id) FROM groups WHERE " +
   1.170 -        "flags & " + Channel.DELETED + " = 0");
   1.171 -      
   1.172 -      // Prepare statements for method delete(article)
   1.173 -      this.pstmtDeleteArticle0 = conn.prepareStatement(
   1.174 -        "DELETE FROM articles WHERE article_id = " +
   1.175 -        "(SELECT article_id FROM article_ids WHERE message_id = ?)");
   1.176 -      this.pstmtDeleteArticle1 = conn.prepareStatement(
   1.177 -        "DELETE FROM headers WHERE article_id = " +
   1.178 -        "(SELECT article_id FROM article_ids WHERE message_id = ?)");
   1.179 -      this.pstmtDeleteArticle2 = conn.prepareStatement(
   1.180 -        "DELETE FROM postings WHERE article_id = " +
   1.181 -        "(SELECT article_id FROM article_ids WHERE message_id = ?)");
   1.182 -      this.pstmtDeleteArticle3 = conn.prepareStatement(
   1.183 -        "DELETE FROM article_ids WHERE message_id = ?");
   1.184 +			// Prepare statements for method addArticle()
   1.185 +			this.pstmtAddArticle1 = conn.prepareStatement(
   1.186 +				"INSERT INTO articles (article_id, body) VALUES(?, ?)");
   1.187 +			this.pstmtAddArticle2 = conn.prepareStatement(
   1.188 +				"INSERT INTO headers (article_id, header_key, header_value, header_index) "
   1.189 +				+ "VALUES (?, ?, ?, ?)");
   1.190 +			this.pstmtAddArticle3 = conn.prepareStatement(
   1.191 +				"INSERT INTO postings (group_id, article_id, article_index)"
   1.192 +				+ "VALUES (?, ?, ?)");
   1.193 +			this.pstmtAddArticle4 = conn.prepareStatement(
   1.194 +				"INSERT INTO article_ids (article_id, message_id) VALUES (?, ?)");
   1.195  
   1.196 -      // Prepare statements for methods getArticle()
   1.197 -      this.pstmtGetArticle0 = conn.prepareStatement(
   1.198 -        "SELECT * FROM articles  WHERE article_id = " +
   1.199 -        "(SELECT article_id FROM article_ids WHERE message_id = ?)");
   1.200 -      this.pstmtGetArticle1 = conn.prepareStatement(
   1.201 -        "SELECT * FROM articles WHERE article_id = " +
   1.202 -        "(SELECT article_id FROM postings WHERE " +
   1.203 -        "article_index = ? AND group_id = ?)");
   1.204 -      
   1.205 -      // Prepare statement for method getArticleHeaders()
   1.206 -      this.pstmtGetArticleHeaders0 = conn.prepareStatement(
   1.207 -        "SELECT header_key, header_value FROM headers WHERE article_id = ? " +
   1.208 -        "ORDER BY header_index ASC");
   1.209 +			// Prepare statement for method addStatValue()
   1.210 +			this.pstmtAddEvent = conn.prepareStatement(
   1.211 +				"INSERT INTO events VALUES (?, ?, ?)");
   1.212  
   1.213 -      // Prepare statement for method getArticleHeaders(regular expr pattern)
   1.214 -      this.pstmtGetArticleHeaders1 = conn.prepareStatement(
   1.215 -        "SELECT p.article_index, h.header_value FROM headers h " +
   1.216 -          "INNER JOIN postings p ON h.article_id = p.article_id " +
   1.217 -          "INNER JOIN groups g ON p.group_id = g.group_id " +
   1.218 -            "WHERE g.name          =  ? AND " +
   1.219 -                  "h.header_key    =  ? AND " +
   1.220 -                  "p.article_index >= ? " +
   1.221 -        "ORDER BY p.article_index ASC");
   1.222 +			// Prepare statement for method addGroup()
   1.223 +			this.pstmtAddGroup0 = conn.prepareStatement(
   1.224 +				"INSERT INTO groups (name, flags) VALUES (?, ?)");
   1.225  
   1.226 -      this.pstmtGetArticleIDs = conn.prepareStatement(
   1.227 -        "SELECT article_index FROM postings WHERE group_id = ?");
   1.228 -      
   1.229 -      // Prepare statement for method getArticleIndex
   1.230 -      this.pstmtGetArticleIndex = conn.prepareStatement(
   1.231 -              "SELECT article_index FROM postings WHERE " +
   1.232 -              "article_id = (SELECT article_id FROM article_ids " +
   1.233 -              "WHERE message_id = ?) " +
   1.234 -              " AND group_id = ?");
   1.235 +			// Prepare statement for method countArticles()
   1.236 +			this.pstmtCountArticles = conn.prepareStatement(
   1.237 +				"SELECT Count(article_id) FROM article_ids");
   1.238  
   1.239 -      // Prepare statements for method getArticleHeads()
   1.240 -      this.pstmtGetArticleHeads = conn.prepareStatement(
   1.241 -        "SELECT article_id, article_index FROM postings WHERE " +
   1.242 -        "postings.group_id = ? AND article_index >= ? AND " +
   1.243 -        "article_index <= ?");
   1.244 +			// Prepare statement for method countGroups()
   1.245 +			this.pstmtCountGroups = conn.prepareStatement(
   1.246 +				"SELECT Count(group_id) FROM groups WHERE "
   1.247 +				+ "flags & " + Channel.DELETED + " = 0");
   1.248  
   1.249 -      // Prepare statements for method getConfigValue()
   1.250 -      this.pstmtGetConfigValue = conn.prepareStatement(
   1.251 -        "SELECT config_value FROM config WHERE config_key = ?");
   1.252 +			// Prepare statements for method delete(article)
   1.253 +			this.pstmtDeleteArticle0 = conn.prepareStatement(
   1.254 +				"DELETE FROM articles WHERE article_id = "
   1.255 +				+ "(SELECT article_id FROM article_ids WHERE message_id = ?)");
   1.256 +			this.pstmtDeleteArticle1 = conn.prepareStatement(
   1.257 +				"DELETE FROM headers WHERE article_id = "
   1.258 +				+ "(SELECT article_id FROM article_ids WHERE message_id = ?)");
   1.259 +			this.pstmtDeleteArticle2 = conn.prepareStatement(
   1.260 +				"DELETE FROM postings WHERE article_id = "
   1.261 +				+ "(SELECT article_id FROM article_ids WHERE message_id = ?)");
   1.262 +			this.pstmtDeleteArticle3 = conn.prepareStatement(
   1.263 +				"DELETE FROM article_ids WHERE message_id = ?");
   1.264  
   1.265 -      // Prepare statements for method getEventsCount()
   1.266 -      this.pstmtGetEventsCount0 = conn.prepareStatement(
   1.267 -        "SELECT Count(*) FROM events WHERE event_key = ? AND " +
   1.268 -        "event_time >= ? AND event_time < ?");
   1.269 +			// Prepare statements for methods getArticle()
   1.270 +			this.pstmtGetArticle0 = conn.prepareStatement(
   1.271 +				"SELECT * FROM articles  WHERE article_id = "
   1.272 +				+ "(SELECT article_id FROM article_ids WHERE message_id = ?)");
   1.273 +			this.pstmtGetArticle1 = conn.prepareStatement(
   1.274 +				"SELECT * FROM articles WHERE article_id = "
   1.275 +				+ "(SELECT article_id FROM postings WHERE "
   1.276 +				+ "article_index = ? AND group_id = ?)");
   1.277  
   1.278 -      this.pstmtGetEventsCount1 = conn.prepareStatement(
   1.279 -        "SELECT Count(*) FROM events WHERE event_key = ? AND " +
   1.280 -        "event_time >= ? AND event_time < ? AND group_id = ?");
   1.281 -      
   1.282 -      // Prepare statement for method getGroupForList()
   1.283 -      this.pstmtGetGroupForList = conn.prepareStatement(
   1.284 -        "SELECT name FROM groups INNER JOIN groups2list " +
   1.285 -        "ON groups.group_id = groups2list.group_id " +
   1.286 -        "WHERE groups2list.listaddress = ?");
   1.287 +			// Prepare statement for method getArticleHeaders()
   1.288 +			this.pstmtGetArticleHeaders0 = conn.prepareStatement(
   1.289 +				"SELECT header_key, header_value FROM headers WHERE article_id = ? "
   1.290 +				+ "ORDER BY header_index ASC");
   1.291  
   1.292 -      // Prepare statement for method getGroup()
   1.293 -      this.pstmtGetGroup0 = conn.prepareStatement(
   1.294 -        "SELECT group_id, flags FROM groups WHERE Name = ?");
   1.295 -      this.pstmtGetGroup1 = conn.prepareStatement(
   1.296 -        "SELECT name FROM groups WHERE group_id = ?");
   1.297 +			// Prepare statement for method getArticleHeaders(regular expr pattern)
   1.298 +			this.pstmtGetArticleHeaders1 = conn.prepareStatement(
   1.299 +				"SELECT p.article_index, h.header_value FROM headers h "
   1.300 +				+ "INNER JOIN postings p ON h.article_id = p.article_id "
   1.301 +				+ "INNER JOIN groups g ON p.group_id = g.group_id "
   1.302 +				+ "WHERE g.name          =  ? AND "
   1.303 +				+ "h.header_key    =  ? AND "
   1.304 +				+ "p.article_index >= ? "
   1.305 +				+ "ORDER BY p.article_index ASC");
   1.306  
   1.307 -      // Prepare statement for method getLastArticleNumber()
   1.308 -      this.pstmtGetLastArticleNumber = conn.prepareStatement(
   1.309 -        "SELECT Max(article_index) FROM postings WHERE group_id = ?");
   1.310 +			this.pstmtGetArticleIDs = conn.prepareStatement(
   1.311 +				"SELECT article_index FROM postings WHERE group_id = ?");
   1.312  
   1.313 -      // Prepare statement for method getListForGroup()
   1.314 -      this.pstmtGetListForGroup = conn.prepareStatement(
   1.315 -        "SELECT listaddress FROM groups2list INNER JOIN groups " +
   1.316 -        "ON groups.group_id = groups2list.group_id WHERE name = ?");
   1.317 +			// Prepare statement for method getArticleIndex
   1.318 +			this.pstmtGetArticleIndex = conn.prepareStatement(
   1.319 +				"SELECT article_index FROM postings WHERE "
   1.320 +				+ "article_id = (SELECT article_id FROM article_ids "
   1.321 +				+ "WHERE message_id = ?) "
   1.322 +				+ " AND group_id = ?");
   1.323  
   1.324 -      // Prepare statement for method getMaxArticleID()
   1.325 -      this.pstmtGetMaxArticleID = conn.prepareStatement(
   1.326 -        "SELECT Max(article_id) FROM articles");
   1.327 -      
   1.328 -      // Prepare statement for method getMaxArticleIndex()
   1.329 -      this.pstmtGetMaxArticleIndex = conn.prepareStatement(
   1.330 -        "SELECT Max(article_index) FROM postings WHERE group_id = ?");
   1.331 -      
   1.332 -      // Prepare statement for method getOldestArticle()
   1.333 -      this.pstmtGetOldestArticle = conn.prepareStatement(
   1.334 -        "SELECT message_id FROM article_ids WHERE article_id = " +
   1.335 -        "(SELECT Min(article_id) FROM article_ids)");
   1.336 +			// Prepare statements for method getArticleHeads()
   1.337 +			this.pstmtGetArticleHeads = conn.prepareStatement(
   1.338 +				"SELECT article_id, article_index FROM postings WHERE "
   1.339 +				+ "postings.group_id = ? AND article_index >= ? AND "
   1.340 +				+ "article_index <= ?");
   1.341  
   1.342 -      // Prepare statement for method getFirstArticleNumber()
   1.343 -      this.pstmtGetFirstArticleNumber = conn.prepareStatement(
   1.344 -        "SELECT Min(article_index) FROM postings WHERE group_id = ?");
   1.345 -      
   1.346 -      // Prepare statement for method getPostingsCount()
   1.347 -      this.pstmtGetPostingsCount = conn.prepareStatement(
   1.348 -        "SELECT Count(*) FROM postings NATURAL JOIN groups " +
   1.349 -        "WHERE groups.name = ?");
   1.350 -      
   1.351 -      // Prepare statement for method getSubscriptions()
   1.352 -      this.pstmtGetSubscriptions = conn.prepareStatement(
   1.353 -        "SELECT host, port, name FROM peers NATURAL JOIN " +
   1.354 -        "peer_subscriptions NATURAL JOIN groups WHERE feedtype = ?");
   1.355 -      
   1.356 -      // Prepare statement for method isArticleExisting()
   1.357 -      this.pstmtIsArticleExisting = conn.prepareStatement(
   1.358 -        "SELECT Count(article_id) FROM article_ids WHERE message_id = ?");
   1.359 -      
   1.360 -      // Prepare statement for method isGroupExisting()
   1.361 -      this.pstmtIsGroupExisting = conn.prepareStatement(
   1.362 -        "SELECT * FROM groups WHERE name = ?");
   1.363 -      
   1.364 -      // Prepare statement for method setConfigValue()
   1.365 -      this.pstmtSetConfigValue0 = conn.prepareStatement(
   1.366 -        "DELETE FROM config WHERE config_key = ?");
   1.367 -      this.pstmtSetConfigValue1 = conn.prepareStatement(
   1.368 -        "INSERT INTO config VALUES(?, ?)");
   1.369 +			// Prepare statements for method getConfigValue()
   1.370 +			this.pstmtGetConfigValue = conn.prepareStatement(
   1.371 +				"SELECT config_value FROM config WHERE config_key = ?");
   1.372  
   1.373 -      // Prepare statements for method purgeGroup()
   1.374 -      this.pstmtPurgeGroup0 = conn.prepareStatement(
   1.375 -        "DELETE FROM peer_subscriptions WHERE group_id = ?");
   1.376 -      this.pstmtPurgeGroup1 = conn.prepareStatement(
   1.377 -        "DELETE FROM groups WHERE group_id = ?");
   1.378 +			// Prepare statements for method getEventsCount()
   1.379 +			this.pstmtGetEventsCount0 = conn.prepareStatement(
   1.380 +				"SELECT Count(*) FROM events WHERE event_key = ? AND "
   1.381 +				+ "event_time >= ? AND event_time < ?");
   1.382  
   1.383 -      // Prepare statement for method update(Group)
   1.384 -      this.pstmtUpdateGroup = conn.prepareStatement(
   1.385 -        "UPDATE groups SET flags = ?, name = ? WHERE group_id = ?");
   1.386 -    }
   1.387 -    catch(ClassNotFoundException ex)
   1.388 -    {
   1.389 -      throw new Error("JDBC Driver not found!", ex);
   1.390 -    }
   1.391 -  }
   1.392 -  
   1.393 -  /**
   1.394 -   * Adds an article to the database.
   1.395 -   * @param article
   1.396 -   * @return
   1.397 -   * @throws java.sql.SQLException
   1.398 -   */
   1.399 -  @Override
   1.400 -  public void addArticle(final Article article)
   1.401 -    throws StorageBackendException
   1.402 -  {
   1.403 -    try
   1.404 -    {
   1.405 -      this.conn.setAutoCommit(false);
   1.406 +			this.pstmtGetEventsCount1 = conn.prepareStatement(
   1.407 +				"SELECT Count(*) FROM events WHERE event_key = ? AND "
   1.408 +				+ "event_time >= ? AND event_time < ? AND group_id = ?");
   1.409  
   1.410 -      int newArticleID = getMaxArticleID() + 1;
   1.411 +			// Prepare statement for method getGroupForList()
   1.412 +			this.pstmtGetGroupForList = conn.prepareStatement(
   1.413 +				"SELECT name FROM groups INNER JOIN groups2list "
   1.414 +				+ "ON groups.group_id = groups2list.group_id "
   1.415 +				+ "WHERE groups2list.listaddress = ?");
   1.416  
   1.417 -      // Fill prepared statement with values;
   1.418 -      // writes body to article table
   1.419 -      pstmtAddArticle1.setInt(1, newArticleID);
   1.420 -      pstmtAddArticle1.setBytes(2, article.getBody());
   1.421 -      pstmtAddArticle1.execute();
   1.422 +			// Prepare statement for method getGroup()
   1.423 +			this.pstmtGetGroup0 = conn.prepareStatement(
   1.424 +				"SELECT group_id, flags FROM groups WHERE Name = ?");
   1.425 +			this.pstmtGetGroup1 = conn.prepareStatement(
   1.426 +				"SELECT name FROM groups WHERE group_id = ?");
   1.427  
   1.428 -      // Add headers
   1.429 -      Enumeration headers = article.getAllHeaders();
   1.430 -      for(int n = 0; headers.hasMoreElements(); n++)
   1.431 -      {
   1.432 -        Header header = (Header)headers.nextElement();
   1.433 -        pstmtAddArticle2.setInt(1, newArticleID);
   1.434 -        pstmtAddArticle2.setString(2, header.getName().toLowerCase());
   1.435 -        pstmtAddArticle2.setString(3, 
   1.436 -          header.getValue().replaceAll("[\r\n]", ""));
   1.437 -        pstmtAddArticle2.setInt(4, n);
   1.438 -        pstmtAddArticle2.execute();
   1.439 -      }
   1.440 -      
   1.441 -      // For each newsgroup add a reference
   1.442 -      List<Group> groups = article.getGroups();
   1.443 -      for(Group group : groups)
   1.444 -      {
   1.445 -        pstmtAddArticle3.setLong(1, group.getInternalID());
   1.446 -        pstmtAddArticle3.setInt(2, newArticleID);
   1.447 -        pstmtAddArticle3.setLong(3, getMaxArticleIndex(group.getInternalID()) + 1);
   1.448 -        pstmtAddArticle3.execute();
   1.449 -      }
   1.450 -      
   1.451 -      // Write message-id to article_ids table
   1.452 -      this.pstmtAddArticle4.setInt(1, newArticleID);
   1.453 -      this.pstmtAddArticle4.setString(2, article.getMessageID());
   1.454 -      this.pstmtAddArticle4.execute();
   1.455 +			// Prepare statement for method getLastArticleNumber()
   1.456 +			this.pstmtGetLastArticleNumber = conn.prepareStatement(
   1.457 +				"SELECT Max(article_index) FROM postings WHERE group_id = ?");
   1.458  
   1.459 -      this.conn.commit();
   1.460 -      this.conn.setAutoCommit(true);
   1.461 +			// Prepare statement for method getListForGroup()
   1.462 +			this.pstmtGetListForGroup = conn.prepareStatement(
   1.463 +				"SELECT listaddress FROM groups2list INNER JOIN groups "
   1.464 +				+ "ON groups.group_id = groups2list.group_id WHERE name = ?");
   1.465  
   1.466 -      this.restarts = 0; // Reset error count
   1.467 -    }
   1.468 -    catch(SQLException ex)
   1.469 -    {
   1.470 -      try
   1.471 -      {
   1.472 -        this.conn.rollback();  // Rollback changes
   1.473 -      }
   1.474 -      catch(SQLException ex2)
   1.475 -      {
   1.476 -        Log.get().severe("Rollback of addArticle() failed: " + ex2);
   1.477 -      }
   1.478 -      
   1.479 -      try
   1.480 -      {
   1.481 -        this.conn.setAutoCommit(true); // and release locks
   1.482 -      }
   1.483 -      catch(SQLException ex2)
   1.484 -      {
   1.485 -        Log.get().severe("setAutoCommit(true) of addArticle() failed: " + ex2);
   1.486 -      }
   1.487 +			// Prepare statement for method getMaxArticleID()
   1.488 +			this.pstmtGetMaxArticleID = conn.prepareStatement(
   1.489 +				"SELECT Max(article_id) FROM articles");
   1.490  
   1.491 -      restartConnection(ex);
   1.492 -      addArticle(article);
   1.493 -    }
   1.494 -  }
   1.495 -  
   1.496 -  /**
   1.497 -   * Adds a group to the JDBCDatabase. This method is not accessible via NNTP.
   1.498 -   * @param name
   1.499 -   * @throws java.sql.SQLException
   1.500 -   */
   1.501 -  @Override
   1.502 -  public void addGroup(String name, int flags)
   1.503 -    throws StorageBackendException
   1.504 -  {
   1.505 -    try
   1.506 -    {
   1.507 -      this.conn.setAutoCommit(false);
   1.508 -      pstmtAddGroup0.setString(1, name);
   1.509 -      pstmtAddGroup0.setInt(2, flags);
   1.510 +			// Prepare statement for method getMaxArticleIndex()
   1.511 +			this.pstmtGetMaxArticleIndex = conn.prepareStatement(
   1.512 +				"SELECT Max(article_index) FROM postings WHERE group_id = ?");
   1.513  
   1.514 -      pstmtAddGroup0.executeUpdate();
   1.515 -      this.conn.commit();
   1.516 -      this.conn.setAutoCommit(true);
   1.517 -      this.restarts = 0; // Reset error count
   1.518 -    }
   1.519 -    catch(SQLException ex)
   1.520 -    {
   1.521 -      try
   1.522 -      {
   1.523 -        this.conn.rollback();
   1.524 -        this.conn.setAutoCommit(true);
   1.525 -      }
   1.526 -      catch(SQLException ex2)
   1.527 -      {
   1.528 -        ex2.printStackTrace();
   1.529 -      }
   1.530 +			// Prepare statement for method getOldestArticle()
   1.531 +			this.pstmtGetOldestArticle = conn.prepareStatement(
   1.532 +				"SELECT message_id FROM article_ids WHERE article_id = "
   1.533 +				+ "(SELECT Min(article_id) FROM article_ids)");
   1.534  
   1.535 -      restartConnection(ex);
   1.536 -      addGroup(name, flags);
   1.537 -    }
   1.538 -  }
   1.539 +			// Prepare statement for method getFirstArticleNumber()
   1.540 +			this.pstmtGetFirstArticleNumber = conn.prepareStatement(
   1.541 +				"SELECT Min(article_index) FROM postings WHERE group_id = ?");
   1.542  
   1.543 -  @Override
   1.544 -  public void addEvent(long time, int type, long gid)
   1.545 -    throws StorageBackendException
   1.546 -  {
   1.547 -    try
   1.548 -    {
   1.549 -      this.conn.setAutoCommit(false);
   1.550 -      this.pstmtAddEvent.setLong(1, time);
   1.551 -      this.pstmtAddEvent.setInt(2, type);
   1.552 -      this.pstmtAddEvent.setLong(3, gid);
   1.553 -      this.pstmtAddEvent.executeUpdate();
   1.554 -      this.conn.commit();
   1.555 -      this.conn.setAutoCommit(true);
   1.556 -      this.restarts = 0;
   1.557 -    }
   1.558 -    catch(SQLException ex)
   1.559 -    {
   1.560 -      try
   1.561 -      {
   1.562 -        this.conn.rollback();
   1.563 -        this.conn.setAutoCommit(true);
   1.564 -      }
   1.565 -      catch(SQLException ex2)
   1.566 -      {
   1.567 -        ex2.printStackTrace();
   1.568 -      }
   1.569 +			// Prepare statement for method getPostingsCount()
   1.570 +			this.pstmtGetPostingsCount = conn.prepareStatement(
   1.571 +				"SELECT Count(*) FROM postings NATURAL JOIN groups "
   1.572 +				+ "WHERE groups.name = ?");
   1.573  
   1.574 -      restartConnection(ex);
   1.575 -      addEvent(time, type, gid);
   1.576 -    }
   1.577 -  }
   1.578 +			// Prepare statement for method getSubscriptions()
   1.579 +			this.pstmtGetSubscriptions = conn.prepareStatement(
   1.580 +				"SELECT host, port, name FROM peers NATURAL JOIN "
   1.581 +				+ "peer_subscriptions NATURAL JOIN groups WHERE feedtype = ?");
   1.582  
   1.583 -  @Override
   1.584 -  public int countArticles()
   1.585 -    throws StorageBackendException
   1.586 -  {
   1.587 -    ResultSet rs = null;
   1.588 +			// Prepare statement for method isArticleExisting()
   1.589 +			this.pstmtIsArticleExisting = conn.prepareStatement(
   1.590 +				"SELECT Count(article_id) FROM article_ids WHERE message_id = ?");
   1.591  
   1.592 -    try
   1.593 -    {
   1.594 -      rs = this.pstmtCountArticles.executeQuery();
   1.595 -      if(rs.next())
   1.596 -      {
   1.597 -        return rs.getInt(1);
   1.598 -      }
   1.599 -      else
   1.600 -      {
   1.601 -        return -1;
   1.602 -      }
   1.603 -    }
   1.604 -    catch(SQLException ex)
   1.605 -    {
   1.606 -      restartConnection(ex);
   1.607 -      return countArticles();
   1.608 -    }
   1.609 -    finally
   1.610 -    {
   1.611 -      if(rs != null)
   1.612 -      {
   1.613 -        try
   1.614 -        {
   1.615 -          rs.close();
   1.616 -        }
   1.617 -        catch(SQLException ex)
   1.618 -        {
   1.619 -          ex.printStackTrace();
   1.620 -        }
   1.621 -        restarts = 0;
   1.622 -      }
   1.623 -    }
   1.624 -  }
   1.625 +			// Prepare statement for method isGroupExisting()
   1.626 +			this.pstmtIsGroupExisting = conn.prepareStatement(
   1.627 +				"SELECT * FROM groups WHERE name = ?");
   1.628  
   1.629 -  @Override
   1.630 -  public int countGroups()
   1.631 -    throws StorageBackendException
   1.632 -  {
   1.633 -    ResultSet rs = null;
   1.634 +			// Prepare statement for method setConfigValue()
   1.635 +			this.pstmtSetConfigValue0 = conn.prepareStatement(
   1.636 +				"DELETE FROM config WHERE config_key = ?");
   1.637 +			this.pstmtSetConfigValue1 = conn.prepareStatement(
   1.638 +				"INSERT INTO config VALUES(?, ?)");
   1.639  
   1.640 -    try
   1.641 -    {
   1.642 -      rs = this.pstmtCountGroups.executeQuery();
   1.643 -      if(rs.next())
   1.644 -      {
   1.645 -        return rs.getInt(1);
   1.646 -      }
   1.647 -      else
   1.648 -      {
   1.649 -        return -1;
   1.650 -      }
   1.651 -    }
   1.652 -    catch(SQLException ex)
   1.653 -    {
   1.654 -      restartConnection(ex);
   1.655 -      return countGroups();
   1.656 -    }
   1.657 -    finally
   1.658 -    {
   1.659 -      if(rs != null)
   1.660 -      {
   1.661 -        try
   1.662 -        {
   1.663 -          rs.close();
   1.664 -        }
   1.665 -        catch(SQLException ex)
   1.666 -        {
   1.667 -          ex.printStackTrace();
   1.668 -        }
   1.669 -        restarts = 0;
   1.670 -      }
   1.671 -    }
   1.672 -  }
   1.673 +			// Prepare statements for method purgeGroup()
   1.674 +			this.pstmtPurgeGroup0 = conn.prepareStatement(
   1.675 +				"DELETE FROM peer_subscriptions WHERE group_id = ?");
   1.676 +			this.pstmtPurgeGroup1 = conn.prepareStatement(
   1.677 +				"DELETE FROM groups WHERE group_id = ?");
   1.678  
   1.679 -  @Override
   1.680 -  public void delete(final String messageID)
   1.681 -    throws StorageBackendException
   1.682 -  {
   1.683 -    try
   1.684 -    {
   1.685 -      this.conn.setAutoCommit(false);
   1.686 -      
   1.687 -      this.pstmtDeleteArticle0.setString(1, messageID);
   1.688 -      int rs = this.pstmtDeleteArticle0.executeUpdate();
   1.689 -      
   1.690 -      // We do not trust the ON DELETE CASCADE functionality to delete
   1.691 -      // orphaned references...
   1.692 -      this.pstmtDeleteArticle1.setString(1, messageID);
   1.693 -      rs = this.pstmtDeleteArticle1.executeUpdate();
   1.694 +			// Prepare statement for method update(Group)
   1.695 +			this.pstmtUpdateGroup = conn.prepareStatement(
   1.696 +				"UPDATE groups SET flags = ?, name = ? WHERE group_id = ?");
   1.697 +		} catch (ClassNotFoundException ex) {
   1.698 +			throw new Error("JDBC Driver not found!", ex);
   1.699 +		}
   1.700 +	}
   1.701  
   1.702 -      this.pstmtDeleteArticle2.setString(1, messageID);
   1.703 -      rs = this.pstmtDeleteArticle2.executeUpdate();
   1.704 +	/**
   1.705 +	 * Adds an article to the database.
   1.706 +	 * @param article
   1.707 +	 * @return
   1.708 +	 * @throws java.sql.SQLException
   1.709 +	 */
   1.710 +	@Override
   1.711 +	public void addArticle(final Article article)
   1.712 +		throws StorageBackendException
   1.713 +	{
   1.714 +		try {
   1.715 +			this.conn.setAutoCommit(false);
   1.716  
   1.717 -      this.pstmtDeleteArticle3.setString(1, messageID);
   1.718 -      rs = this.pstmtDeleteArticle3.executeUpdate();
   1.719 -      
   1.720 -      this.conn.commit();
   1.721 -      this.conn.setAutoCommit(true);
   1.722 -    }
   1.723 -    catch(SQLException ex)
   1.724 -    {
   1.725 -      throw new StorageBackendException(ex);
   1.726 -    }
   1.727 -  }
   1.728 +			int newArticleID = getMaxArticleID() + 1;
   1.729  
   1.730 -  @Override
   1.731 -  public Article getArticle(String messageID)
   1.732 -    throws StorageBackendException
   1.733 -  {
   1.734 -    ResultSet rs = null;
   1.735 -    try
   1.736 -    {
   1.737 -      pstmtGetArticle0.setString(1, messageID);
   1.738 -      rs = pstmtGetArticle0.executeQuery();
   1.739 +			// Fill prepared statement with values;
   1.740 +			// writes body to article table
   1.741 +			pstmtAddArticle1.setInt(1, newArticleID);
   1.742 +			pstmtAddArticle1.setBytes(2, article.getBody());
   1.743 +			pstmtAddArticle1.execute();
   1.744  
   1.745 -      if(!rs.next())
   1.746 -      {
   1.747 -        return null;
   1.748 -      }
   1.749 -      else
   1.750 -      {
   1.751 -        byte[] body     = rs.getBytes("body");
   1.752 -        String headers  = getArticleHeaders(rs.getInt("article_id"));
   1.753 -        return new Article(headers, body);
   1.754 -      }
   1.755 -    }
   1.756 -    catch(SQLException ex)
   1.757 -    {
   1.758 -      restartConnection(ex);
   1.759 -      return getArticle(messageID);
   1.760 -    }
   1.761 -    finally
   1.762 -    {
   1.763 -      if(rs != null)
   1.764 -      {
   1.765 -        try
   1.766 -        {
   1.767 -          rs.close();
   1.768 -        }
   1.769 -        catch(SQLException ex)
   1.770 -        {
   1.771 -          ex.printStackTrace();
   1.772 -        }
   1.773 -        restarts = 0; // Reset error count
   1.774 -      }
   1.775 -    }
   1.776 -  }
   1.777 -  
   1.778 -  /**
   1.779 -   * Retrieves an article by its ID.
   1.780 -   * @param articleID
   1.781 -   * @return
   1.782 -   * @throws StorageBackendException
   1.783 -   */
   1.784 -  @Override
   1.785 -  public Article getArticle(long articleIndex, long gid)
   1.786 -    throws StorageBackendException
   1.787 -  {  
   1.788 -    ResultSet rs = null;
   1.789 +			// Add headers
   1.790 +			Enumeration headers = article.getAllHeaders();
   1.791 +			for (int n = 0; headers.hasMoreElements(); n++) {
   1.792 +				Header header = (Header) headers.nextElement();
   1.793 +				pstmtAddArticle2.setInt(1, newArticleID);
   1.794 +				pstmtAddArticle2.setString(2, header.getName().toLowerCase());
   1.795 +				pstmtAddArticle2.setString(3,
   1.796 +					header.getValue().replaceAll("[\r\n]", ""));
   1.797 +				pstmtAddArticle2.setInt(4, n);
   1.798 +				pstmtAddArticle2.execute();
   1.799 +			}
   1.800  
   1.801 -    try
   1.802 -    {
   1.803 -      this.pstmtGetArticle1.setLong(1, articleIndex);
   1.804 -      this.pstmtGetArticle1.setLong(2, gid);
   1.805 +			// For each newsgroup add a reference
   1.806 +			List<Group> groups = article.getGroups();
   1.807 +			for (Group group : groups) {
   1.808 +				pstmtAddArticle3.setLong(1, group.getInternalID());
   1.809 +				pstmtAddArticle3.setInt(2, newArticleID);
   1.810 +				pstmtAddArticle3.setLong(3, getMaxArticleIndex(group.getInternalID()) + 1);
   1.811 +				pstmtAddArticle3.execute();
   1.812 +			}
   1.813  
   1.814 -      rs = this.pstmtGetArticle1.executeQuery();
   1.815 +			// Write message-id to article_ids table
   1.816 +			this.pstmtAddArticle4.setInt(1, newArticleID);
   1.817 +			this.pstmtAddArticle4.setString(2, article.getMessageID());
   1.818 +			this.pstmtAddArticle4.execute();
   1.819  
   1.820 -      if(rs.next())
   1.821 -      {
   1.822 -        byte[] body    = rs.getBytes("body");
   1.823 -        String headers = getArticleHeaders(rs.getInt("article_id"));
   1.824 -        return new Article(headers, body);
   1.825 -      }
   1.826 -      else
   1.827 -      {
   1.828 -        return null;
   1.829 -      }
   1.830 -    }
   1.831 -    catch(SQLException ex)
   1.832 -    {
   1.833 -      restartConnection(ex);
   1.834 -      return getArticle(articleIndex, gid);
   1.835 -    }
   1.836 -    finally
   1.837 -    {
   1.838 -      if(rs != null)
   1.839 -      {
   1.840 -        try
   1.841 -        {
   1.842 -          rs.close();
   1.843 -        }
   1.844 -        catch(SQLException ex)
   1.845 -        {
   1.846 -          ex.printStackTrace();
   1.847 -        }
   1.848 -        restarts = 0;
   1.849 -      }
   1.850 -    }
   1.851 -  }
   1.852 +			this.conn.commit();
   1.853 +			this.conn.setAutoCommit(true);
   1.854  
   1.855 -  /**
   1.856 -   * Searches for fitting header values using the given regular expression.
   1.857 -   * @param group
   1.858 -   * @param start
   1.859 -   * @param end
   1.860 -   * @param headerKey
   1.861 -   * @param pattern
   1.862 -   * @return
   1.863 -   * @throws StorageBackendException
   1.864 -   */
   1.865 -  @Override
   1.866 -  public List<Pair<Long, String>> getArticleHeaders(Channel group, long start,
   1.867 -    long end, String headerKey, String patStr)
   1.868 -    throws StorageBackendException, PatternSyntaxException
   1.869 -  {
   1.870 -    ResultSet rs = null;
   1.871 -    List<Pair<Long, String>> heads = new ArrayList<Pair<Long, String>>();
   1.872 +			this.restarts = 0; // Reset error count
   1.873 +		} catch (SQLException ex) {
   1.874 +			try {
   1.875 +				this.conn.rollback();  // Rollback changes
   1.876 +			} catch (SQLException ex2) {
   1.877 +				Log.get().severe("Rollback of addArticle() failed: " + ex2);
   1.878 +			}
   1.879  
   1.880 -    try
   1.881 -    {
   1.882 -      this.pstmtGetArticleHeaders1.setString(1, group.getName());
   1.883 -      this.pstmtGetArticleHeaders1.setString(2, headerKey);
   1.884 -      this.pstmtGetArticleHeaders1.setLong(3, start);
   1.885 +			try {
   1.886 +				this.conn.setAutoCommit(true); // and release locks
   1.887 +			} catch (SQLException ex2) {
   1.888 +				Log.get().severe("setAutoCommit(true) of addArticle() failed: " + ex2);
   1.889 +			}
   1.890  
   1.891 -      rs = this.pstmtGetArticleHeaders1.executeQuery();
   1.892 +			restartConnection(ex);
   1.893 +			addArticle(article);
   1.894 +		}
   1.895 +	}
   1.896  
   1.897 -      // Convert the "NNTP" regex to Java regex
   1.898 -      patStr = patStr.replace("*", ".*");
   1.899 -      Pattern pattern = Pattern.compile(patStr);
   1.900 +	/**
   1.901 +	 * Adds a group to the JDBCDatabase. This method is not accessible via NNTP.
   1.902 +	 * @param name
   1.903 +	 * @throws java.sql.SQLException
   1.904 +	 */
   1.905 +	@Override
   1.906 +	public void addGroup(String name, int flags)
   1.907 +		throws StorageBackendException
   1.908 +	{
   1.909 +		try {
   1.910 +			this.conn.setAutoCommit(false);
   1.911 +			pstmtAddGroup0.setString(1, name);
   1.912 +			pstmtAddGroup0.setInt(2, flags);
   1.913  
   1.914 -      while(rs.next())
   1.915 -      {
   1.916 -        Long articleIndex = rs.getLong(1);
   1.917 -        if(end < 0 || articleIndex <= end) // Match start is done via SQL
   1.918 -        {
   1.919 -          String headerValue  = rs.getString(2);
   1.920 -          Matcher matcher = pattern.matcher(headerValue);
   1.921 -          if(matcher.matches())
   1.922 -          {
   1.923 -            heads.add(new Pair<Long, String>(articleIndex, headerValue));
   1.924 -          }
   1.925 -        }
   1.926 -      }
   1.927 -    }
   1.928 -    catch(SQLException ex)
   1.929 -    {
   1.930 -      restartConnection(ex);
   1.931 -      return getArticleHeaders(group, start, end, headerKey, patStr);
   1.932 -    }
   1.933 -    finally
   1.934 -    {
   1.935 -      if(rs != null)
   1.936 -      {
   1.937 -        try
   1.938 -        {
   1.939 -          rs.close();
   1.940 -        }
   1.941 -        catch(SQLException ex)
   1.942 -        {
   1.943 -          ex.printStackTrace();
   1.944 -        }
   1.945 -      }
   1.946 -    }
   1.947 +			pstmtAddGroup0.executeUpdate();
   1.948 +			this.conn.commit();
   1.949 +			this.conn.setAutoCommit(true);
   1.950 +			this.restarts = 0; // Reset error count
   1.951 +		} catch (SQLException ex) {
   1.952 +			try {
   1.953 +				this.conn.rollback();
   1.954 +				this.conn.setAutoCommit(true);
   1.955 +			} catch (SQLException ex2) {
   1.956 +				ex2.printStackTrace();
   1.957 +			}
   1.958  
   1.959 -    return heads;
   1.960 -  }
   1.961 +			restartConnection(ex);
   1.962 +			addGroup(name, flags);
   1.963 +		}
   1.964 +	}
   1.965  
   1.966 -  private String getArticleHeaders(long articleID)
   1.967 -    throws StorageBackendException
   1.968 -  {
   1.969 -    ResultSet rs = null;
   1.970 -    
   1.971 -    try
   1.972 -    {
   1.973 -      this.pstmtGetArticleHeaders0.setLong(1, articleID);
   1.974 -      rs = this.pstmtGetArticleHeaders0.executeQuery();
   1.975 -      
   1.976 -      StringBuilder buf = new StringBuilder();
   1.977 -      if(rs.next())
   1.978 -      {
   1.979 -        for(;;)
   1.980 -        {
   1.981 -          buf.append(rs.getString(1)); // key
   1.982 -          buf.append(": ");
   1.983 -          String foldedValue = MimeUtility.fold(0, rs.getString(2));
   1.984 -          buf.append(foldedValue); // value
   1.985 -          if(rs.next())
   1.986 -          {
   1.987 -            buf.append("\r\n");
   1.988 -          }
   1.989 -          else
   1.990 -          {
   1.991 -            break;
   1.992 -          }
   1.993 -        }
   1.994 -      }
   1.995 -      
   1.996 -      return buf.toString();
   1.997 -    }
   1.998 -    catch(SQLException ex)
   1.999 -    {
  1.1000 -      restartConnection(ex);
  1.1001 -      return getArticleHeaders(articleID);
  1.1002 -    }
  1.1003 -    finally
  1.1004 -    {
  1.1005 -      if(rs != null)
  1.1006 -      {
  1.1007 -        try
  1.1008 -        {
  1.1009 -          rs.close();
  1.1010 -        }
  1.1011 -        catch(SQLException ex)
  1.1012 -        {
  1.1013 -          ex.printStackTrace();
  1.1014 -        }
  1.1015 -      }
  1.1016 -    }
  1.1017 -  }
  1.1018 +	@Override
  1.1019 +	public void addEvent(long time, int type, long gid)
  1.1020 +		throws StorageBackendException
  1.1021 +	{
  1.1022 +		try {
  1.1023 +			this.conn.setAutoCommit(false);
  1.1024 +			this.pstmtAddEvent.setLong(1, time);
  1.1025 +			this.pstmtAddEvent.setInt(2, type);
  1.1026 +			this.pstmtAddEvent.setLong(3, gid);
  1.1027 +			this.pstmtAddEvent.executeUpdate();
  1.1028 +			this.conn.commit();
  1.1029 +			this.conn.setAutoCommit(true);
  1.1030 +			this.restarts = 0;
  1.1031 +		} catch (SQLException ex) {
  1.1032 +			try {
  1.1033 +				this.conn.rollback();
  1.1034 +				this.conn.setAutoCommit(true);
  1.1035 +			} catch (SQLException ex2) {
  1.1036 +				ex2.printStackTrace();
  1.1037 +			}
  1.1038  
  1.1039 -  @Override
  1.1040 -  public long getArticleIndex(Article article, Group group)
  1.1041 -    throws StorageBackendException
  1.1042 -  {
  1.1043 -    ResultSet rs = null;
  1.1044 +			restartConnection(ex);
  1.1045 +			addEvent(time, type, gid);
  1.1046 +		}
  1.1047 +	}
  1.1048  
  1.1049 -    try
  1.1050 -    {
  1.1051 -      this.pstmtGetArticleIndex.setString(1, article.getMessageID());
  1.1052 -      this.pstmtGetArticleIndex.setLong(2, group.getInternalID());
  1.1053 -      
  1.1054 -      rs = this.pstmtGetArticleIndex.executeQuery();
  1.1055 -      if(rs.next())
  1.1056 -      {
  1.1057 -        return rs.getLong(1);
  1.1058 -      }
  1.1059 -      else
  1.1060 -      {
  1.1061 -        return -1;
  1.1062 -      }
  1.1063 -    }
  1.1064 -    catch(SQLException ex)
  1.1065 -    {
  1.1066 -      restartConnection(ex);
  1.1067 -      return getArticleIndex(article, group);
  1.1068 -    }
  1.1069 -    finally
  1.1070 -    {
  1.1071 -      if(rs != null)
  1.1072 -      {
  1.1073 -        try
  1.1074 -        {
  1.1075 -          rs.close();
  1.1076 -        }
  1.1077 -        catch(SQLException ex)
  1.1078 -        {
  1.1079 -          ex.printStackTrace();
  1.1080 -        }
  1.1081 -      }
  1.1082 -    }
  1.1083 -  }
  1.1084 -  
  1.1085 -  /**
  1.1086 -   * Returns a list of Long/Article Pairs.
  1.1087 -   * @throws java.sql.SQLException
  1.1088 -   */
  1.1089 -  @Override
  1.1090 -  public List<Pair<Long, ArticleHead>> getArticleHeads(Group group, long first,
  1.1091 -    long last)
  1.1092 -    throws StorageBackendException
  1.1093 -  {
  1.1094 -    ResultSet rs = null;
  1.1095 +	@Override
  1.1096 +	public int countArticles()
  1.1097 +		throws StorageBackendException
  1.1098 +	{
  1.1099 +		ResultSet rs = null;
  1.1100  
  1.1101 -    try
  1.1102 -    {
  1.1103 -      this.pstmtGetArticleHeads.setLong(1, group.getInternalID());
  1.1104 -      this.pstmtGetArticleHeads.setLong(2, first);
  1.1105 -      this.pstmtGetArticleHeads.setLong(3, last);
  1.1106 -      rs = pstmtGetArticleHeads.executeQuery();
  1.1107 +		try {
  1.1108 +			rs = this.pstmtCountArticles.executeQuery();
  1.1109 +			if (rs.next()) {
  1.1110 +				return rs.getInt(1);
  1.1111 +			} else {
  1.1112 +				return -1;
  1.1113 +			}
  1.1114 +		} catch (SQLException ex) {
  1.1115 +			restartConnection(ex);
  1.1116 +			return countArticles();
  1.1117 +		} finally {
  1.1118 +			if (rs != null) {
  1.1119 +				try {
  1.1120 +					rs.close();
  1.1121 +				} catch (SQLException ex) {
  1.1122 +					ex.printStackTrace();
  1.1123 +				}
  1.1124 +				restarts = 0;
  1.1125 +			}
  1.1126 +		}
  1.1127 +	}
  1.1128  
  1.1129 -      List<Pair<Long, ArticleHead>> articles 
  1.1130 -        = new ArrayList<Pair<Long, ArticleHead>>();
  1.1131 +	@Override
  1.1132 +	public int countGroups()
  1.1133 +		throws StorageBackendException
  1.1134 +	{
  1.1135 +		ResultSet rs = null;
  1.1136  
  1.1137 -      while (rs.next())
  1.1138 -      {
  1.1139 -        long aid  = rs.getLong("article_id");
  1.1140 -        long aidx = rs.getLong("article_index");
  1.1141 -        String headers = getArticleHeaders(aid);
  1.1142 -        articles.add(new Pair<Long, ArticleHead>(aidx, 
  1.1143 -                        new ArticleHead(headers)));
  1.1144 -      }
  1.1145 +		try {
  1.1146 +			rs = this.pstmtCountGroups.executeQuery();
  1.1147 +			if (rs.next()) {
  1.1148 +				return rs.getInt(1);
  1.1149 +			} else {
  1.1150 +				return -1;
  1.1151 +			}
  1.1152 +		} catch (SQLException ex) {
  1.1153 +			restartConnection(ex);
  1.1154 +			return countGroups();
  1.1155 +		} finally {
  1.1156 +			if (rs != null) {
  1.1157 +				try {
  1.1158 +					rs.close();
  1.1159 +				} catch (SQLException ex) {
  1.1160 +					ex.printStackTrace();
  1.1161 +				}
  1.1162 +				restarts = 0;
  1.1163 +			}
  1.1164 +		}
  1.1165 +	}
  1.1166  
  1.1167 -      return articles;
  1.1168 -    }
  1.1169 -    catch(SQLException ex)
  1.1170 -    {
  1.1171 -      restartConnection(ex);
  1.1172 -      return getArticleHeads(group, first, last);
  1.1173 -    }
  1.1174 -    finally
  1.1175 -    {
  1.1176 -      if(rs != null)
  1.1177 -      {
  1.1178 -        try
  1.1179 -        {
  1.1180 -          rs.close();
  1.1181 -        }
  1.1182 -        catch(SQLException ex)
  1.1183 -        {
  1.1184 -          ex.printStackTrace();
  1.1185 -        }
  1.1186 -      }
  1.1187 -    }
  1.1188 -  }
  1.1189 +	@Override
  1.1190 +	public void delete(final String messageID)
  1.1191 +		throws StorageBackendException
  1.1192 +	{
  1.1193 +		try {
  1.1194 +			this.conn.setAutoCommit(false);
  1.1195  
  1.1196 -  @Override
  1.1197 -  public List<Long> getArticleNumbers(long gid)
  1.1198 -    throws StorageBackendException
  1.1199 -  {
  1.1200 -    ResultSet rs = null;
  1.1201 -    try
  1.1202 -    {
  1.1203 -      List<Long> ids = new ArrayList<Long>();
  1.1204 -      this.pstmtGetArticleIDs.setLong(1, gid);
  1.1205 -      rs = this.pstmtGetArticleIDs.executeQuery();
  1.1206 -      while(rs.next())
  1.1207 -      {
  1.1208 -        ids.add(rs.getLong(1));
  1.1209 -      }
  1.1210 -      return ids;
  1.1211 -    }
  1.1212 -    catch(SQLException ex)
  1.1213 -    {
  1.1214 -      restartConnection(ex);
  1.1215 -      return getArticleNumbers(gid);
  1.1216 -    }
  1.1217 -    finally
  1.1218 -    {
  1.1219 -      if(rs != null)
  1.1220 -      {
  1.1221 -        try
  1.1222 -        {
  1.1223 -          rs.close();
  1.1224 -          restarts = 0; // Clear the restart count after successful request
  1.1225 -        }
  1.1226 -        catch(SQLException ex)
  1.1227 -        {
  1.1228 -          ex.printStackTrace();
  1.1229 -        }
  1.1230 -      }
  1.1231 -    }
  1.1232 -  }
  1.1233 +			this.pstmtDeleteArticle0.setString(1, messageID);
  1.1234 +			int rs = this.pstmtDeleteArticle0.executeUpdate();
  1.1235  
  1.1236 -  @Override
  1.1237 -  public String getConfigValue(String key)
  1.1238 -    throws StorageBackendException
  1.1239 -  {
  1.1240 -    ResultSet rs = null;
  1.1241 -    try
  1.1242 -    {
  1.1243 -      this.pstmtGetConfigValue.setString(1, key);
  1.1244 +			// We do not trust the ON DELETE CASCADE functionality to delete
  1.1245 +			// orphaned references...
  1.1246 +			this.pstmtDeleteArticle1.setString(1, messageID);
  1.1247 +			rs = this.pstmtDeleteArticle1.executeUpdate();
  1.1248  
  1.1249 -      rs = this.pstmtGetConfigValue.executeQuery();
  1.1250 -      if(rs.next())
  1.1251 -      {
  1.1252 -        return rs.getString(1); // First data on index 1 not 0
  1.1253 -      }
  1.1254 -      else
  1.1255 -      {
  1.1256 -        return null;
  1.1257 -      }
  1.1258 -    }
  1.1259 -    catch(SQLException ex)
  1.1260 -    {
  1.1261 -      restartConnection(ex);
  1.1262 -      return getConfigValue(key);
  1.1263 -    }
  1.1264 -    finally
  1.1265 -    {
  1.1266 -      if(rs != null)
  1.1267 -      {
  1.1268 -        try
  1.1269 -        {
  1.1270 -          rs.close();
  1.1271 -        }
  1.1272 -        catch(SQLException ex)
  1.1273 -        {
  1.1274 -          ex.printStackTrace();
  1.1275 -        }
  1.1276 -        restarts = 0; // Clear the restart count after successful request
  1.1277 -      }
  1.1278 -    }
  1.1279 -  }
  1.1280 +			this.pstmtDeleteArticle2.setString(1, messageID);
  1.1281 +			rs = this.pstmtDeleteArticle2.executeUpdate();
  1.1282  
  1.1283 -  @Override
  1.1284 -  public int getEventsCount(int type, long start, long end, Channel channel)
  1.1285 -    throws StorageBackendException
  1.1286 -  {
  1.1287 -    ResultSet rs = null;
  1.1288 -    
  1.1289 -    try
  1.1290 -    {
  1.1291 -      if(channel == null)
  1.1292 -      {
  1.1293 -        this.pstmtGetEventsCount0.setInt(1, type);
  1.1294 -        this.pstmtGetEventsCount0.setLong(2, start);
  1.1295 -        this.pstmtGetEventsCount0.setLong(3, end);
  1.1296 -        rs = this.pstmtGetEventsCount0.executeQuery();
  1.1297 -      }
  1.1298 -      else
  1.1299 -      {
  1.1300 -        this.pstmtGetEventsCount1.setInt(1, type);
  1.1301 -        this.pstmtGetEventsCount1.setLong(2, start);
  1.1302 -        this.pstmtGetEventsCount1.setLong(3, end);
  1.1303 -        this.pstmtGetEventsCount1.setLong(4, channel.getInternalID());
  1.1304 -        rs = this.pstmtGetEventsCount1.executeQuery();
  1.1305 -      }
  1.1306 -      
  1.1307 -      if(rs.next())
  1.1308 -      {
  1.1309 -        return rs.getInt(1);
  1.1310 -      }
  1.1311 -      else
  1.1312 -      {
  1.1313 -        return -1;
  1.1314 -      }
  1.1315 -    }
  1.1316 -    catch(SQLException ex)
  1.1317 -    {
  1.1318 -      restartConnection(ex);
  1.1319 -      return getEventsCount(type, start, end, channel);
  1.1320 -    }
  1.1321 -    finally
  1.1322 -    {
  1.1323 -      if(rs != null)
  1.1324 -      {
  1.1325 -        try
  1.1326 -        {
  1.1327 -          rs.close();
  1.1328 -        }
  1.1329 -        catch(SQLException ex)
  1.1330 -        {
  1.1331 -          ex.printStackTrace();
  1.1332 -        }
  1.1333 -      }
  1.1334 -    }
  1.1335 -  }
  1.1336 -  
  1.1337 -  /**
  1.1338 -   * Reads all Groups from the JDBCDatabase.
  1.1339 -   * @return
  1.1340 -   * @throws StorageBackendException
  1.1341 -   */
  1.1342 -  @Override
  1.1343 -  public List<Channel> getGroups()
  1.1344 -    throws StorageBackendException
  1.1345 -  {
  1.1346 -    ResultSet   rs;
  1.1347 -    List<Channel> buffer = new ArrayList<Channel>();
  1.1348 -    Statement   stmt   = null;
  1.1349 +			this.pstmtDeleteArticle3.setString(1, messageID);
  1.1350 +			rs = this.pstmtDeleteArticle3.executeUpdate();
  1.1351  
  1.1352 -    try
  1.1353 -    {
  1.1354 -      stmt = conn.createStatement();
  1.1355 -      rs = stmt.executeQuery("SELECT * FROM groups ORDER BY name");
  1.1356 +			this.conn.commit();
  1.1357 +			this.conn.setAutoCommit(true);
  1.1358 +		} catch (SQLException ex) {
  1.1359 +			throw new StorageBackendException(ex);
  1.1360 +		}
  1.1361 +	}
  1.1362  
  1.1363 -      while(rs.next())
  1.1364 -      {
  1.1365 -        String name  = rs.getString("name");
  1.1366 -        long   id    = rs.getLong("group_id");
  1.1367 -        int    flags = rs.getInt("flags");
  1.1368 -        
  1.1369 -        Group group = new Group(name, id, flags);
  1.1370 -        buffer.add(group);
  1.1371 -      }
  1.1372 +	@Override
  1.1373 +	public Article getArticle(String messageID)
  1.1374 +		throws StorageBackendException
  1.1375 +	{
  1.1376 +		ResultSet rs = null;
  1.1377 +		try {
  1.1378 +			pstmtGetArticle0.setString(1, messageID);
  1.1379 +			rs = pstmtGetArticle0.executeQuery();
  1.1380  
  1.1381 -      return buffer;
  1.1382 -    }
  1.1383 -    catch(SQLException ex)
  1.1384 -    {
  1.1385 -      restartConnection(ex);
  1.1386 -      return getGroups();
  1.1387 -    }
  1.1388 -    finally
  1.1389 -    {
  1.1390 -      if(stmt != null)
  1.1391 -      {
  1.1392 -        try
  1.1393 -        {
  1.1394 -          stmt.close(); // Implicitely closes ResultSets
  1.1395 -        }
  1.1396 -        catch(SQLException ex)
  1.1397 -        {
  1.1398 -          ex.printStackTrace();
  1.1399 -        }
  1.1400 -      }
  1.1401 -    }
  1.1402 -  }
  1.1403 +			if (!rs.next()) {
  1.1404 +				return null;
  1.1405 +			} else {
  1.1406 +				byte[] body = rs.getBytes("body");
  1.1407 +				String headers = getArticleHeaders(rs.getInt("article_id"));
  1.1408 +				return new Article(headers, body);
  1.1409 +			}
  1.1410 +		} catch (SQLException ex) {
  1.1411 +			restartConnection(ex);
  1.1412 +			return getArticle(messageID);
  1.1413 +		} finally {
  1.1414 +			if (rs != null) {
  1.1415 +				try {
  1.1416 +					rs.close();
  1.1417 +				} catch (SQLException ex) {
  1.1418 +					ex.printStackTrace();
  1.1419 +				}
  1.1420 +				restarts = 0; // Reset error count
  1.1421 +			}
  1.1422 +		}
  1.1423 +	}
  1.1424  
  1.1425 -  @Override
  1.1426 -  public List<String> getGroupsForList(String listAddress)
  1.1427 -    throws StorageBackendException
  1.1428 -  {
  1.1429 -    ResultSet rs = null;
  1.1430 -    
  1.1431 -    try
  1.1432 -    {
  1.1433 -      this.pstmtGetGroupForList.setString(1, listAddress);
  1.1434 +	/**
  1.1435 +	 * Retrieves an article by its ID.
  1.1436 +	 * @param articleID
  1.1437 +	 * @return
  1.1438 +	 * @throws StorageBackendException
  1.1439 +	 */
  1.1440 +	@Override
  1.1441 +	public Article getArticle(long articleIndex, long gid)
  1.1442 +		throws StorageBackendException
  1.1443 +	{
  1.1444 +		ResultSet rs = null;
  1.1445  
  1.1446 -      rs = this.pstmtGetGroupForList.executeQuery();
  1.1447 -      List<String> groups = new ArrayList<String>();
  1.1448 -      while(rs.next())
  1.1449 -      {
  1.1450 -        String group = rs.getString(1);
  1.1451 -        groups.add(group);
  1.1452 -      }
  1.1453 -      return groups;
  1.1454 -    }
  1.1455 -    catch(SQLException ex)
  1.1456 -    {
  1.1457 -      restartConnection(ex);
  1.1458 -      return getGroupsForList(listAddress);
  1.1459 -    }
  1.1460 -    finally
  1.1461 -    {
  1.1462 -      if(rs != null)
  1.1463 -      {
  1.1464 -        try
  1.1465 -        {
  1.1466 -          rs.close();
  1.1467 -        }
  1.1468 -        catch(SQLException ex)
  1.1469 -        {
  1.1470 -          ex.printStackTrace();
  1.1471 -        }
  1.1472 -      }
  1.1473 -    }
  1.1474 -  }
  1.1475 -  
  1.1476 -  /**
  1.1477 -   * Returns the Group that is identified by the name.
  1.1478 -   * @param name
  1.1479 -   * @return
  1.1480 -   * @throws StorageBackendException
  1.1481 -   */
  1.1482 -  @Override
  1.1483 -  public Group getGroup(String name)
  1.1484 -    throws StorageBackendException
  1.1485 -  {
  1.1486 -    ResultSet rs = null;
  1.1487 -    
  1.1488 -    try
  1.1489 -    {
  1.1490 -      this.pstmtGetGroup0.setString(1, name);
  1.1491 -      rs = this.pstmtGetGroup0.executeQuery();
  1.1492 +		try {
  1.1493 +			this.pstmtGetArticle1.setLong(1, articleIndex);
  1.1494 +			this.pstmtGetArticle1.setLong(2, gid);
  1.1495  
  1.1496 -      if (!rs.next())
  1.1497 -      {
  1.1498 -        return null;
  1.1499 -      }
  1.1500 -      else
  1.1501 -      {
  1.1502 -        long id = rs.getLong("group_id");
  1.1503 -        int flags = rs.getInt("flags");
  1.1504 -        return new Group(name, id, flags);
  1.1505 -      }
  1.1506 -    }
  1.1507 -    catch(SQLException ex)
  1.1508 -    {
  1.1509 -      restartConnection(ex);
  1.1510 -      return getGroup(name);
  1.1511 -    }
  1.1512 -    finally
  1.1513 -    {
  1.1514 -      if(rs != null)
  1.1515 -      {
  1.1516 -        try
  1.1517 -        {
  1.1518 -          rs.close();
  1.1519 -        }
  1.1520 -        catch(SQLException ex)
  1.1521 -        {
  1.1522 -          ex.printStackTrace();
  1.1523 -        }
  1.1524 -      }
  1.1525 -    }
  1.1526 -  }
  1.1527 +			rs = this.pstmtGetArticle1.executeQuery();
  1.1528  
  1.1529 -  @Override
  1.1530 -  public List<String> getListsForGroup(String group)
  1.1531 -    throws StorageBackendException
  1.1532 -  {
  1.1533 -    ResultSet     rs    = null;
  1.1534 -    List<String>  lists = new ArrayList<String>();
  1.1535 +			if (rs.next()) {
  1.1536 +				byte[] body = rs.getBytes("body");
  1.1537 +				String headers = getArticleHeaders(rs.getInt("article_id"));
  1.1538 +				return new Article(headers, body);
  1.1539 +			} else {
  1.1540 +				return null;
  1.1541 +			}
  1.1542 +		} catch (SQLException ex) {
  1.1543 +			restartConnection(ex);
  1.1544 +			return getArticle(articleIndex, gid);
  1.1545 +		} finally {
  1.1546 +			if (rs != null) {
  1.1547 +				try {
  1.1548 +					rs.close();
  1.1549 +				} catch (SQLException ex) {
  1.1550 +					ex.printStackTrace();
  1.1551 +				}
  1.1552 +				restarts = 0;
  1.1553 +			}
  1.1554 +		}
  1.1555 +	}
  1.1556  
  1.1557 -    try
  1.1558 -    {
  1.1559 -      this.pstmtGetListForGroup.setString(1, group);
  1.1560 -      rs = this.pstmtGetListForGroup.executeQuery();
  1.1561 +	/**
  1.1562 +	 * Searches for fitting header values using the given regular expression.
  1.1563 +	 * @param group
  1.1564 +	 * @param start
  1.1565 +	 * @param end
  1.1566 +	 * @param headerKey
  1.1567 +	 * @param pattern
  1.1568 +	 * @return
  1.1569 +	 * @throws StorageBackendException
  1.1570 +	 */
  1.1571 +	@Override
  1.1572 +	public List<Pair<Long, String>> getArticleHeaders(Channel group, long start,
  1.1573 +		long end, String headerKey, String patStr)
  1.1574 +		throws StorageBackendException, PatternSyntaxException
  1.1575 +	{
  1.1576 +		ResultSet rs = null;
  1.1577 +		List<Pair<Long, String>> heads = new ArrayList<Pair<Long, String>>();
  1.1578  
  1.1579 -      while(rs.next())
  1.1580 -      {
  1.1581 -        lists.add(rs.getString(1));
  1.1582 -      }
  1.1583 -      return lists;
  1.1584 -    }
  1.1585 -    catch(SQLException ex)
  1.1586 -    {
  1.1587 -      restartConnection(ex);
  1.1588 -      return getListsForGroup(group);
  1.1589 -    }
  1.1590 -    finally
  1.1591 -    {
  1.1592 -      if(rs != null)
  1.1593 -      {
  1.1594 -        try
  1.1595 -        {
  1.1596 -          rs.close();
  1.1597 -        }
  1.1598 -        catch(SQLException ex)
  1.1599 -        {
  1.1600 -          ex.printStackTrace();
  1.1601 -        }
  1.1602 -      }
  1.1603 -    }
  1.1604 -  }
  1.1605 -  
  1.1606 -  private int getMaxArticleIndex(long groupID)
  1.1607 -    throws StorageBackendException
  1.1608 -  {
  1.1609 -    ResultSet rs    = null;
  1.1610 +		try {
  1.1611 +			this.pstmtGetArticleHeaders1.setString(1, group.getName());
  1.1612 +			this.pstmtGetArticleHeaders1.setString(2, headerKey);
  1.1613 +			this.pstmtGetArticleHeaders1.setLong(3, start);
  1.1614  
  1.1615 -    try
  1.1616 -    {
  1.1617 -      this.pstmtGetMaxArticleIndex.setLong(1, groupID);
  1.1618 -      rs = this.pstmtGetMaxArticleIndex.executeQuery();
  1.1619 +			rs = this.pstmtGetArticleHeaders1.executeQuery();
  1.1620  
  1.1621 -      int maxIndex = 0;
  1.1622 -      if (rs.next())
  1.1623 -      {
  1.1624 -        maxIndex = rs.getInt(1);
  1.1625 -      }
  1.1626 +			// Convert the "NNTP" regex to Java regex
  1.1627 +			patStr = patStr.replace("*", ".*");
  1.1628 +			Pattern pattern = Pattern.compile(patStr);
  1.1629  
  1.1630 -      return maxIndex;
  1.1631 -    }
  1.1632 -    catch(SQLException ex)
  1.1633 -    {
  1.1634 -      restartConnection(ex);
  1.1635 -      return getMaxArticleIndex(groupID);
  1.1636 -    }
  1.1637 -    finally
  1.1638 -    {
  1.1639 -      if(rs != null)
  1.1640 -      {
  1.1641 -        try
  1.1642 -        {
  1.1643 -          rs.close();
  1.1644 -        }
  1.1645 -        catch(SQLException ex)
  1.1646 -        {
  1.1647 -          ex.printStackTrace();
  1.1648 -        }
  1.1649 -      }
  1.1650 -    }
  1.1651 -  }
  1.1652 -  
  1.1653 -  private int getMaxArticleID()
  1.1654 -    throws StorageBackendException
  1.1655 -  {
  1.1656 -    ResultSet rs    = null;
  1.1657 +			while (rs.next()) {
  1.1658 +				Long articleIndex = rs.getLong(1);
  1.1659 +				if (end < 0 || articleIndex <= end) // Match start is done via SQL
  1.1660 +				{
  1.1661 +					String headerValue = rs.getString(2);
  1.1662 +					Matcher matcher = pattern.matcher(headerValue);
  1.1663 +					if (matcher.matches()) {
  1.1664 +						heads.add(new Pair<Long, String>(articleIndex, headerValue));
  1.1665 +					}
  1.1666 +				}
  1.1667 +			}
  1.1668 +		} catch (SQLException ex) {
  1.1669 +			restartConnection(ex);
  1.1670 +			return getArticleHeaders(group, start, end, headerKey, patStr);
  1.1671 +		} finally {
  1.1672 +			if (rs != null) {
  1.1673 +				try {
  1.1674 +					rs.close();
  1.1675 +				} catch (SQLException ex) {
  1.1676 +					ex.printStackTrace();
  1.1677 +				}
  1.1678 +			}
  1.1679 +		}
  1.1680  
  1.1681 -    try
  1.1682 -    {
  1.1683 -      rs = this.pstmtGetMaxArticleID.executeQuery();
  1.1684 +		return heads;
  1.1685 +	}
  1.1686  
  1.1687 -      int maxIndex = 0;
  1.1688 -      if (rs.next())
  1.1689 -      {
  1.1690 -        maxIndex = rs.getInt(1);
  1.1691 -      }
  1.1692 +	private String getArticleHeaders(long articleID)
  1.1693 +		throws StorageBackendException
  1.1694 +	{
  1.1695 +		ResultSet rs = null;
  1.1696  
  1.1697 -      return maxIndex;
  1.1698 -    }
  1.1699 -    catch(SQLException ex)
  1.1700 -    {
  1.1701 -      restartConnection(ex);
  1.1702 -      return getMaxArticleID();
  1.1703 -    }
  1.1704 -    finally
  1.1705 -    {
  1.1706 -      if(rs != null)
  1.1707 -      {
  1.1708 -        try
  1.1709 -        {
  1.1710 -          rs.close();
  1.1711 -        }
  1.1712 -        catch(SQLException ex)
  1.1713 -        {
  1.1714 -          ex.printStackTrace();
  1.1715 -        }
  1.1716 -      }
  1.1717 -    }
  1.1718 -  }
  1.1719 +		try {
  1.1720 +			this.pstmtGetArticleHeaders0.setLong(1, articleID);
  1.1721 +			rs = this.pstmtGetArticleHeaders0.executeQuery();
  1.1722  
  1.1723 -  @Override
  1.1724 -  public int getLastArticleNumber(Group group)
  1.1725 -    throws StorageBackendException
  1.1726 -  {
  1.1727 -    ResultSet rs = null;
  1.1728 +			StringBuilder buf = new StringBuilder();
  1.1729 +			if (rs.next()) {
  1.1730 +				for (;;) {
  1.1731 +					buf.append(rs.getString(1)); // key
  1.1732 +					buf.append(": ");
  1.1733 +					String foldedValue = MimeUtility.fold(0, rs.getString(2));
  1.1734 +					buf.append(foldedValue); // value
  1.1735 +					if (rs.next()) {
  1.1736 +						buf.append("\r\n");
  1.1737 +					} else {
  1.1738 +						break;
  1.1739 +					}
  1.1740 +				}
  1.1741 +			}
  1.1742  
  1.1743 -    try
  1.1744 -    {
  1.1745 -      this.pstmtGetLastArticleNumber.setLong(1, group.getInternalID());
  1.1746 -      rs = this.pstmtGetLastArticleNumber.executeQuery();
  1.1747 -      if (rs.next())
  1.1748 -      {
  1.1749 -        return rs.getInt(1);
  1.1750 -      }
  1.1751 -      else
  1.1752 -      {
  1.1753 -        return 0;
  1.1754 -      }
  1.1755 -    }
  1.1756 -    catch(SQLException ex)
  1.1757 -    {
  1.1758 -      restartConnection(ex);
  1.1759 -      return getLastArticleNumber(group);
  1.1760 -    }
  1.1761 -    finally
  1.1762 -    {
  1.1763 -      if(rs != null)
  1.1764 -      {
  1.1765 -        try
  1.1766 -        {
  1.1767 -          rs.close();
  1.1768 -        }
  1.1769 -        catch(SQLException ex)
  1.1770 -        {
  1.1771 -          ex.printStackTrace();
  1.1772 -        }
  1.1773 -      }
  1.1774 -    }
  1.1775 -  }
  1.1776 +			return buf.toString();
  1.1777 +		} catch (SQLException ex) {
  1.1778 +			restartConnection(ex);
  1.1779 +			return getArticleHeaders(articleID);
  1.1780 +		} finally {
  1.1781 +			if (rs != null) {
  1.1782 +				try {
  1.1783 +					rs.close();
  1.1784 +				} catch (SQLException ex) {
  1.1785 +					ex.printStackTrace();
  1.1786 +				}
  1.1787 +			}
  1.1788 +		}
  1.1789 +	}
  1.1790  
  1.1791 -  @Override
  1.1792 -  public int getFirstArticleNumber(Group group)
  1.1793 -    throws StorageBackendException
  1.1794 -  {
  1.1795 -    ResultSet rs = null;
  1.1796 -    try
  1.1797 -    {
  1.1798 -      this.pstmtGetFirstArticleNumber.setLong(1, group.getInternalID());
  1.1799 -      rs = this.pstmtGetFirstArticleNumber.executeQuery();
  1.1800 -      if(rs.next())
  1.1801 -      {
  1.1802 -        return rs.getInt(1);
  1.1803 -      }
  1.1804 -      else
  1.1805 -      {
  1.1806 -        return 0;
  1.1807 -      }
  1.1808 -    }
  1.1809 -    catch(SQLException ex)
  1.1810 -    {
  1.1811 -      restartConnection(ex);
  1.1812 -      return getFirstArticleNumber(group);
  1.1813 -    }
  1.1814 -    finally
  1.1815 -    {
  1.1816 -      if(rs != null)
  1.1817 -      {
  1.1818 -        try
  1.1819 -        {
  1.1820 -          rs.close();
  1.1821 -        }
  1.1822 -        catch(SQLException ex)
  1.1823 -        {
  1.1824 -          ex.printStackTrace();
  1.1825 -        }
  1.1826 -      }
  1.1827 -    }
  1.1828 -  }
  1.1829 -  
  1.1830 -  /**
  1.1831 -   * Returns a group name identified by the given id.
  1.1832 -   * @param id
  1.1833 -   * @return
  1.1834 -   * @throws StorageBackendException
  1.1835 -   */
  1.1836 -  public String getGroup(int id)
  1.1837 -    throws StorageBackendException
  1.1838 -  {
  1.1839 -    ResultSet rs = null;
  1.1840 +	@Override
  1.1841 +	public long getArticleIndex(Article article, Group group)
  1.1842 +		throws StorageBackendException
  1.1843 +	{
  1.1844 +		ResultSet rs = null;
  1.1845  
  1.1846 -    try
  1.1847 -    {
  1.1848 -      this.pstmtGetGroup1.setInt(1, id);
  1.1849 -      rs = this.pstmtGetGroup1.executeQuery();
  1.1850 +		try {
  1.1851 +			this.pstmtGetArticleIndex.setString(1, article.getMessageID());
  1.1852 +			this.pstmtGetArticleIndex.setLong(2, group.getInternalID());
  1.1853  
  1.1854 -      if (rs.next())
  1.1855 -      {
  1.1856 -        return rs.getString(1);
  1.1857 -      }
  1.1858 -      else
  1.1859 -      {
  1.1860 -        return null;
  1.1861 -      }
  1.1862 -    }
  1.1863 -    catch(SQLException ex)
  1.1864 -    {
  1.1865 -      restartConnection(ex);
  1.1866 -      return getGroup(id);
  1.1867 -    }
  1.1868 -    finally
  1.1869 -    {
  1.1870 -      if(rs != null)
  1.1871 -      {
  1.1872 -        try
  1.1873 -        {
  1.1874 -          rs.close();
  1.1875 -        }
  1.1876 -        catch(SQLException ex)
  1.1877 -        {
  1.1878 -          ex.printStackTrace();
  1.1879 -        }
  1.1880 -      }
  1.1881 -    }
  1.1882 -  }
  1.1883 +			rs = this.pstmtGetArticleIndex.executeQuery();
  1.1884 +			if (rs.next()) {
  1.1885 +				return rs.getLong(1);
  1.1886 +			} else {
  1.1887 +				return -1;
  1.1888 +			}
  1.1889 +		} catch (SQLException ex) {
  1.1890 +			restartConnection(ex);
  1.1891 +			return getArticleIndex(article, group);
  1.1892 +		} finally {
  1.1893 +			if (rs != null) {
  1.1894 +				try {
  1.1895 +					rs.close();
  1.1896 +				} catch (SQLException ex) {
  1.1897 +					ex.printStackTrace();
  1.1898 +				}
  1.1899 +			}
  1.1900 +		}
  1.1901 +	}
  1.1902  
  1.1903 -  @Override
  1.1904 -  public double getEventsPerHour(int key, long gid)
  1.1905 -    throws StorageBackendException
  1.1906 -  {
  1.1907 -    String gidquery = "";
  1.1908 -    if(gid >= 0)
  1.1909 -    {
  1.1910 -      gidquery = " AND group_id = " + gid;
  1.1911 -    }
  1.1912 -    
  1.1913 -    Statement stmt = null;
  1.1914 -    ResultSet rs   = null;
  1.1915 -    
  1.1916 -    try
  1.1917 -    {
  1.1918 -      stmt = this.conn.createStatement();
  1.1919 -      rs = stmt.executeQuery("SELECT Count(*) / (Max(event_time) - Min(event_time))" +
  1.1920 -        " * 1000 * 60 * 60 FROM events WHERE event_key = " + key + gidquery);
  1.1921 -      
  1.1922 -      if(rs.next())
  1.1923 -      {
  1.1924 -        restarts = 0; // reset error count
  1.1925 -        return rs.getDouble(1);
  1.1926 -      }
  1.1927 -      else
  1.1928 -      {
  1.1929 -        return Double.NaN;
  1.1930 -      }
  1.1931 -    }
  1.1932 -    catch(SQLException ex)
  1.1933 -    {
  1.1934 -      restartConnection(ex);
  1.1935 -      return getEventsPerHour(key, gid);
  1.1936 -    }
  1.1937 -    finally
  1.1938 -    {
  1.1939 -      try
  1.1940 -      {
  1.1941 -        if(stmt != null)
  1.1942 -        {
  1.1943 -          stmt.close(); // Implicitely closes the result sets
  1.1944 -        }
  1.1945 -      }
  1.1946 -      catch(SQLException ex)
  1.1947 -      {
  1.1948 -        ex.printStackTrace();
  1.1949 -      }
  1.1950 -    }
  1.1951 -  }
  1.1952 +	/**
  1.1953 +	 * Returns a list of Long/Article Pairs.
  1.1954 +	 * @throws java.sql.SQLException
  1.1955 +	 */
  1.1956 +	@Override
  1.1957 +	public List<Pair<Long, ArticleHead>> getArticleHeads(Group group, long first,
  1.1958 +		long last)
  1.1959 +		throws StorageBackendException
  1.1960 +	{
  1.1961 +		ResultSet rs = null;
  1.1962  
  1.1963 -  @Override
  1.1964 -  public String getOldestArticle()
  1.1965 -    throws StorageBackendException
  1.1966 -  {
  1.1967 -    ResultSet rs = null;
  1.1968 +		try {
  1.1969 +			this.pstmtGetArticleHeads.setLong(1, group.getInternalID());
  1.1970 +			this.pstmtGetArticleHeads.setLong(2, first);
  1.1971 +			this.pstmtGetArticleHeads.setLong(3, last);
  1.1972 +			rs = pstmtGetArticleHeads.executeQuery();
  1.1973  
  1.1974 -    try
  1.1975 -    {
  1.1976 -      rs = this.pstmtGetOldestArticle.executeQuery();
  1.1977 -      if(rs.next())
  1.1978 -      {
  1.1979 -        return rs.getString(1);
  1.1980 -      }
  1.1981 -      else
  1.1982 -      {
  1.1983 -        return null;
  1.1984 -      }
  1.1985 -    }
  1.1986 -    catch(SQLException ex)
  1.1987 -    {
  1.1988 -      restartConnection(ex);
  1.1989 -      return getOldestArticle();
  1.1990 -    }
  1.1991 -    finally
  1.1992 -    {
  1.1993 -      if(rs != null)
  1.1994 -      {
  1.1995 -        try
  1.1996 -        {
  1.1997 -          rs.close();
  1.1998 -        }
  1.1999 -        catch(SQLException ex)
  1.2000 -        {
  1.2001 -          ex.printStackTrace();
  1.2002 -        }
  1.2003 -      }
  1.2004 -    }
  1.2005 -  }
  1.2006 +			List<Pair<Long, ArticleHead>> articles = new ArrayList<Pair<Long, ArticleHead>>();
  1.2007  
  1.2008 -  @Override
  1.2009 -  public int getPostingsCount(String groupname)
  1.2010 -    throws StorageBackendException
  1.2011 -  {
  1.2012 -    ResultSet rs = null;
  1.2013 -    
  1.2014 -    try
  1.2015 -    {
  1.2016 -      this.pstmtGetPostingsCount.setString(1, groupname);
  1.2017 -      rs = this.pstmtGetPostingsCount.executeQuery();
  1.2018 -      if(rs.next())
  1.2019 -      {
  1.2020 -        return rs.getInt(1);
  1.2021 -      }
  1.2022 -      else
  1.2023 -      {
  1.2024 -        Log.get().warning("Count on postings return nothing!");
  1.2025 -        return 0;
  1.2026 -      }
  1.2027 -    }
  1.2028 -    catch(SQLException ex)
  1.2029 -    {
  1.2030 -      restartConnection(ex);
  1.2031 -      return getPostingsCount(groupname);
  1.2032 -    }
  1.2033 -    finally
  1.2034 -    {
  1.2035 -      if(rs != null)
  1.2036 -      {
  1.2037 -        try
  1.2038 -        {
  1.2039 -          rs.close();
  1.2040 -        }
  1.2041 -        catch(SQLException ex)
  1.2042 -        {
  1.2043 -          ex.printStackTrace();
  1.2044 -        }
  1.2045 -      }
  1.2046 -    }
  1.2047 -  }
  1.2048 +			while (rs.next()) {
  1.2049 +				long aid = rs.getLong("article_id");
  1.2050 +				long aidx = rs.getLong("article_index");
  1.2051 +				String headers = getArticleHeaders(aid);
  1.2052 +				articles.add(new Pair<Long, ArticleHead>(aidx,
  1.2053 +					new ArticleHead(headers)));
  1.2054 +			}
  1.2055  
  1.2056 -  @Override
  1.2057 -  public List<Subscription> getSubscriptions(int feedtype)
  1.2058 -    throws StorageBackendException
  1.2059 -  {
  1.2060 -    ResultSet rs = null;
  1.2061 -    
  1.2062 -    try
  1.2063 -    {
  1.2064 -      List<Subscription> subs = new ArrayList<Subscription>();
  1.2065 -      this.pstmtGetSubscriptions.setInt(1, feedtype);
  1.2066 -      rs = this.pstmtGetSubscriptions.executeQuery();
  1.2067 -      
  1.2068 -      while(rs.next())
  1.2069 -      {
  1.2070 -        String host  = rs.getString("host");
  1.2071 -        String group = rs.getString("name");
  1.2072 -        int    port  = rs.getInt("port");
  1.2073 -        subs.add(new Subscription(host, port, feedtype, group));
  1.2074 -      }
  1.2075 -      
  1.2076 -      return subs;
  1.2077 -    }
  1.2078 -    catch(SQLException ex)
  1.2079 -    {
  1.2080 -      restartConnection(ex);
  1.2081 -      return getSubscriptions(feedtype);
  1.2082 -    }
  1.2083 -    finally
  1.2084 -    {
  1.2085 -      if(rs != null)
  1.2086 -      {
  1.2087 -        try
  1.2088 -        {
  1.2089 -          rs.close();
  1.2090 -        }
  1.2091 -        catch(SQLException ex)
  1.2092 -        {
  1.2093 -          ex.printStackTrace();
  1.2094 -        }
  1.2095 -      }
  1.2096 -    }
  1.2097 -  }
  1.2098 +			return articles;
  1.2099 +		} catch (SQLException ex) {
  1.2100 +			restartConnection(ex);
  1.2101 +			return getArticleHeads(group, first, last);
  1.2102 +		} finally {
  1.2103 +			if (rs != null) {
  1.2104 +				try {
  1.2105 +					rs.close();
  1.2106 +				} catch (SQLException ex) {
  1.2107 +					ex.printStackTrace();
  1.2108 +				}
  1.2109 +			}
  1.2110 +		}
  1.2111 +	}
  1.2112  
  1.2113 -  /**
  1.2114 -   * Checks if there is an article with the given messageid in the JDBCDatabase.
  1.2115 -   * @param name
  1.2116 -   * @return
  1.2117 -   * @throws StorageBackendException
  1.2118 -   */
  1.2119 -  @Override
  1.2120 -  public boolean isArticleExisting(String messageID)
  1.2121 -    throws StorageBackendException
  1.2122 -  {
  1.2123 -    ResultSet rs = null;
  1.2124 -    
  1.2125 -    try
  1.2126 -    {
  1.2127 -      this.pstmtIsArticleExisting.setString(1, messageID);
  1.2128 -      rs = this.pstmtIsArticleExisting.executeQuery();
  1.2129 -      return rs.next() && rs.getInt(1) == 1;
  1.2130 -    }
  1.2131 -    catch(SQLException ex)
  1.2132 -    {
  1.2133 -      restartConnection(ex);
  1.2134 -      return isArticleExisting(messageID);
  1.2135 -    }
  1.2136 -    finally
  1.2137 -    {
  1.2138 -      if(rs != null)
  1.2139 -      {
  1.2140 -        try
  1.2141 -        {
  1.2142 -          rs.close();
  1.2143 -        }
  1.2144 -        catch(SQLException ex)
  1.2145 -        {
  1.2146 -          ex.printStackTrace();
  1.2147 -        }
  1.2148 -      }
  1.2149 -    }
  1.2150 -  }
  1.2151 -  
  1.2152 -  /**
  1.2153 -   * Checks if there is a group with the given name in the JDBCDatabase.
  1.2154 -   * @param name
  1.2155 -   * @return
  1.2156 -   * @throws StorageBackendException
  1.2157 -   */
  1.2158 -  @Override
  1.2159 -  public boolean isGroupExisting(String name)
  1.2160 -    throws StorageBackendException
  1.2161 -  {
  1.2162 -    ResultSet rs = null;
  1.2163 -    
  1.2164 -    try
  1.2165 -    {
  1.2166 -      this.pstmtIsGroupExisting.setString(1, name);
  1.2167 -      rs = this.pstmtIsGroupExisting.executeQuery();
  1.2168 -      return rs.next();
  1.2169 -    }
  1.2170 -    catch(SQLException ex)
  1.2171 -    {
  1.2172 -      restartConnection(ex);
  1.2173 -      return isGroupExisting(name);
  1.2174 -    }
  1.2175 -    finally
  1.2176 -    {
  1.2177 -      if(rs != null)
  1.2178 -      {
  1.2179 -        try
  1.2180 -        {
  1.2181 -          rs.close();
  1.2182 -        }
  1.2183 -        catch(SQLException ex)
  1.2184 -        {
  1.2185 -          ex.printStackTrace();
  1.2186 -        }
  1.2187 -      }
  1.2188 -    }
  1.2189 -  }
  1.2190 +	@Override
  1.2191 +	public List<Long> getArticleNumbers(long gid)
  1.2192 +		throws StorageBackendException
  1.2193 +	{
  1.2194 +		ResultSet rs = null;
  1.2195 +		try {
  1.2196 +			List<Long> ids = new ArrayList<Long>();
  1.2197 +			this.pstmtGetArticleIDs.setLong(1, gid);
  1.2198 +			rs = this.pstmtGetArticleIDs.executeQuery();
  1.2199 +			while (rs.next()) {
  1.2200 +				ids.add(rs.getLong(1));
  1.2201 +			}
  1.2202 +			return ids;
  1.2203 +		} catch (SQLException ex) {
  1.2204 +			restartConnection(ex);
  1.2205 +			return getArticleNumbers(gid);
  1.2206 +		} finally {
  1.2207 +			if (rs != null) {
  1.2208 +				try {
  1.2209 +					rs.close();
  1.2210 +					restarts = 0; // Clear the restart count after successful request
  1.2211 +				} catch (SQLException ex) {
  1.2212 +					ex.printStackTrace();
  1.2213 +				}
  1.2214 +			}
  1.2215 +		}
  1.2216 +	}
  1.2217  
  1.2218 -  @Override
  1.2219 -  public void setConfigValue(String key, String value)
  1.2220 -    throws StorageBackendException
  1.2221 -  {
  1.2222 -    try
  1.2223 -    {
  1.2224 -      conn.setAutoCommit(false);
  1.2225 -      this.pstmtSetConfigValue0.setString(1, key);
  1.2226 -      this.pstmtSetConfigValue0.execute();
  1.2227 -      this.pstmtSetConfigValue1.setString(1, key);
  1.2228 -      this.pstmtSetConfigValue1.setString(2, value);
  1.2229 -      this.pstmtSetConfigValue1.execute();
  1.2230 -      conn.commit();
  1.2231 -      conn.setAutoCommit(true);
  1.2232 -    }
  1.2233 -    catch(SQLException ex)
  1.2234 -    {
  1.2235 -      restartConnection(ex);
  1.2236 -      setConfigValue(key, value);
  1.2237 -    }
  1.2238 -  }
  1.2239 -  
  1.2240 -  /**
  1.2241 -   * Closes the JDBCDatabase connection.
  1.2242 -   */
  1.2243 -  public void shutdown()
  1.2244 -    throws StorageBackendException
  1.2245 -  {
  1.2246 -    try
  1.2247 -    {
  1.2248 -      if(this.conn != null)
  1.2249 -      {
  1.2250 -        this.conn.close();
  1.2251 -      }
  1.2252 -    }
  1.2253 -    catch(SQLException ex)
  1.2254 -    {
  1.2255 -      throw new StorageBackendException(ex);
  1.2256 -    }
  1.2257 -  }
  1.2258 +	@Override
  1.2259 +	public String getConfigValue(String key)
  1.2260 +		throws StorageBackendException
  1.2261 +	{
  1.2262 +		ResultSet rs = null;
  1.2263 +		try {
  1.2264 +			this.pstmtGetConfigValue.setString(1, key);
  1.2265  
  1.2266 -  @Override
  1.2267 -  public void purgeGroup(Group group)
  1.2268 -    throws StorageBackendException
  1.2269 -  {
  1.2270 -    try
  1.2271 -    {
  1.2272 -      this.pstmtPurgeGroup0.setLong(1, group.getInternalID());
  1.2273 -      this.pstmtPurgeGroup0.executeUpdate();
  1.2274 +			rs = this.pstmtGetConfigValue.executeQuery();
  1.2275 +			if (rs.next()) {
  1.2276 +				return rs.getString(1); // First data on index 1 not 0
  1.2277 +			} else {
  1.2278 +				return null;
  1.2279 +			}
  1.2280 +		} catch (SQLException ex) {
  1.2281 +			restartConnection(ex);
  1.2282 +			return getConfigValue(key);
  1.2283 +		} finally {
  1.2284 +			if (rs != null) {
  1.2285 +				try {
  1.2286 +					rs.close();
  1.2287 +				} catch (SQLException ex) {
  1.2288 +					ex.printStackTrace();
  1.2289 +				}
  1.2290 +				restarts = 0; // Clear the restart count after successful request
  1.2291 +			}
  1.2292 +		}
  1.2293 +	}
  1.2294  
  1.2295 -      this.pstmtPurgeGroup1.setLong(1, group.getInternalID());
  1.2296 -      this.pstmtPurgeGroup1.executeUpdate();
  1.2297 -    }
  1.2298 -    catch(SQLException ex)
  1.2299 -    {
  1.2300 -      restartConnection(ex);
  1.2301 -      purgeGroup(group);
  1.2302 -    }
  1.2303 -  }
  1.2304 -  
  1.2305 -  private void restartConnection(SQLException cause)
  1.2306 -    throws StorageBackendException
  1.2307 -  {
  1.2308 -    restarts++;
  1.2309 -    Log.get().severe(Thread.currentThread()
  1.2310 -      + ": Database connection was closed (restart " + restarts + ").");
  1.2311 -    
  1.2312 -    if(restarts >= MAX_RESTARTS)
  1.2313 -    {
  1.2314 -      // Delete the current, probably broken JDBCDatabase instance.
  1.2315 -      // So no one can use the instance any more.
  1.2316 -      JDBCDatabaseProvider.instances.remove(Thread.currentThread());
  1.2317 -      
  1.2318 -      // Throw the exception upwards
  1.2319 -      throw new StorageBackendException(cause);
  1.2320 -    }
  1.2321 -    
  1.2322 -    try
  1.2323 -    {
  1.2324 -      Thread.sleep(1500L * restarts);
  1.2325 -    }
  1.2326 -    catch(InterruptedException ex)
  1.2327 -    {
  1.2328 -      Log.get().warning("Interrupted: " + ex.getMessage());
  1.2329 -    }
  1.2330 -    
  1.2331 -    // Try to properly close the old database connection
  1.2332 -    try
  1.2333 -    {
  1.2334 -      if(this.conn != null)
  1.2335 -      {
  1.2336 -        this.conn.close();
  1.2337 -      }
  1.2338 -    }
  1.2339 -    catch(SQLException ex)
  1.2340 -    {
  1.2341 -      Log.get().warning(ex.getMessage());
  1.2342 -    }
  1.2343 -    
  1.2344 -    try
  1.2345 -    {
  1.2346 -      // Try to reinitialize database connection
  1.2347 -      arise();
  1.2348 -    }
  1.2349 -    catch(SQLException ex)
  1.2350 -    {
  1.2351 -      Log.get().warning(ex.getMessage());
  1.2352 -      restartConnection(ex);
  1.2353 -    }
  1.2354 -  }
  1.2355 +	@Override
  1.2356 +	public int getEventsCount(int type, long start, long end, Channel channel)
  1.2357 +		throws StorageBackendException
  1.2358 +	{
  1.2359 +		ResultSet rs = null;
  1.2360  
  1.2361 -  @Override
  1.2362 -  public boolean update(Article article)
  1.2363 -    throws StorageBackendException
  1.2364 -  {
  1.2365 -    // DELETE FROM headers WHERE article_id = ?
  1.2366 +		try {
  1.2367 +			if (channel == null) {
  1.2368 +				this.pstmtGetEventsCount0.setInt(1, type);
  1.2369 +				this.pstmtGetEventsCount0.setLong(2, start);
  1.2370 +				this.pstmtGetEventsCount0.setLong(3, end);
  1.2371 +				rs = this.pstmtGetEventsCount0.executeQuery();
  1.2372 +			} else {
  1.2373 +				this.pstmtGetEventsCount1.setInt(1, type);
  1.2374 +				this.pstmtGetEventsCount1.setLong(2, start);
  1.2375 +				this.pstmtGetEventsCount1.setLong(3, end);
  1.2376 +				this.pstmtGetEventsCount1.setLong(4, channel.getInternalID());
  1.2377 +				rs = this.pstmtGetEventsCount1.executeQuery();
  1.2378 +			}
  1.2379  
  1.2380 -    // INSERT INTO headers ...
  1.2381 +			if (rs.next()) {
  1.2382 +				return rs.getInt(1);
  1.2383 +			} else {
  1.2384 +				return -1;
  1.2385 +			}
  1.2386 +		} catch (SQLException ex) {
  1.2387 +			restartConnection(ex);
  1.2388 +			return getEventsCount(type, start, end, channel);
  1.2389 +		} finally {
  1.2390 +			if (rs != null) {
  1.2391 +				try {
  1.2392 +					rs.close();
  1.2393 +				} catch (SQLException ex) {
  1.2394 +					ex.printStackTrace();
  1.2395 +				}
  1.2396 +			}
  1.2397 +		}
  1.2398 +	}
  1.2399  
  1.2400 -    // SELECT * FROM postings WHERE article_id = ? AND group_id = ?
  1.2401 -    return false;
  1.2402 -  }
  1.2403 +	/**
  1.2404 +	 * Reads all Groups from the JDBCDatabase.
  1.2405 +	 * @return
  1.2406 +	 * @throws StorageBackendException
  1.2407 +	 */
  1.2408 +	@Override
  1.2409 +	public List<Channel> getGroups()
  1.2410 +		throws StorageBackendException
  1.2411 +	{
  1.2412 +		ResultSet rs;
  1.2413 +		List<Channel> buffer = new ArrayList<Channel>();
  1.2414 +		Statement stmt = null;
  1.2415  
  1.2416 -  /**
  1.2417 -   * Writes the flags and the name of the given group to the database.
  1.2418 -   * @param group
  1.2419 -   * @throws StorageBackendException
  1.2420 -   */
  1.2421 -  @Override
  1.2422 -  public boolean update(Group group)
  1.2423 -    throws StorageBackendException
  1.2424 -  {
  1.2425 -    try
  1.2426 -    {
  1.2427 -      this.pstmtUpdateGroup.setInt(1, group.getFlags());
  1.2428 -      this.pstmtUpdateGroup.setString(2, group.getName());
  1.2429 -      this.pstmtUpdateGroup.setLong(3, group.getInternalID());
  1.2430 -      int rs = this.pstmtUpdateGroup.executeUpdate();
  1.2431 -      return rs == 1;
  1.2432 -    }
  1.2433 -    catch(SQLException ex)
  1.2434 -    {
  1.2435 -      restartConnection(ex);
  1.2436 -      return update(group);
  1.2437 -    }
  1.2438 -  }
  1.2439 +		try {
  1.2440 +			stmt = conn.createStatement();
  1.2441 +			rs = stmt.executeQuery("SELECT * FROM groups ORDER BY name");
  1.2442  
  1.2443 +			while (rs.next()) {
  1.2444 +				String name = rs.getString("name");
  1.2445 +				long id = rs.getLong("group_id");
  1.2446 +				int flags = rs.getInt("flags");
  1.2447 +
  1.2448 +				Group group = new Group(name, id, flags);
  1.2449 +				buffer.add(group);
  1.2450 +			}
  1.2451 +
  1.2452 +			return buffer;
  1.2453 +		} catch (SQLException ex) {
  1.2454 +			restartConnection(ex);
  1.2455 +			return getGroups();
  1.2456 +		} finally {
  1.2457 +			if (stmt != null) {
  1.2458 +				try {
  1.2459 +					stmt.close(); // Implicitely closes ResultSets
  1.2460 +				} catch (SQLException ex) {
  1.2461 +					ex.printStackTrace();
  1.2462 +				}
  1.2463 +			}
  1.2464 +		}
  1.2465 +	}
  1.2466 +
  1.2467 +	@Override
  1.2468 +	public List<String> getGroupsForList(String listAddress)
  1.2469 +		throws StorageBackendException
  1.2470 +	{
  1.2471 +		ResultSet rs = null;
  1.2472 +
  1.2473 +		try {
  1.2474 +			this.pstmtGetGroupForList.setString(1, listAddress);
  1.2475 +
  1.2476 +			rs = this.pstmtGetGroupForList.executeQuery();
  1.2477 +			List<String> groups = new ArrayList<String>();
  1.2478 +			while (rs.next()) {
  1.2479 +				String group = rs.getString(1);
  1.2480 +				groups.add(group);
  1.2481 +			}
  1.2482 +			return groups;
  1.2483 +		} catch (SQLException ex) {
  1.2484 +			restartConnection(ex);
  1.2485 +			return getGroupsForList(listAddress);
  1.2486 +		} finally {
  1.2487 +			if (rs != null) {
  1.2488 +				try {
  1.2489 +					rs.close();
  1.2490 +				} catch (SQLException ex) {
  1.2491 +					ex.printStackTrace();
  1.2492 +				}
  1.2493 +			}
  1.2494 +		}
  1.2495 +	}
  1.2496 +
  1.2497 +	/**
  1.2498 +	 * Returns the Group that is identified by the name.
  1.2499 +	 * @param name
  1.2500 +	 * @return
  1.2501 +	 * @throws StorageBackendException
  1.2502 +	 */
  1.2503 +	@Override
  1.2504 +	public Group getGroup(String name)
  1.2505 +		throws StorageBackendException
  1.2506 +	{
  1.2507 +		ResultSet rs = null;
  1.2508 +
  1.2509 +		try {
  1.2510 +			this.pstmtGetGroup0.setString(1, name);
  1.2511 +			rs = this.pstmtGetGroup0.executeQuery();
  1.2512 +
  1.2513 +			if (!rs.next()) {
  1.2514 +				return null;
  1.2515 +			} else {
  1.2516 +				long id = rs.getLong("group_id");
  1.2517 +				int flags = rs.getInt("flags");
  1.2518 +				return new Group(name, id, flags);
  1.2519 +			}
  1.2520 +		} catch (SQLException ex) {
  1.2521 +			restartConnection(ex);
  1.2522 +			return getGroup(name);
  1.2523 +		} finally {
  1.2524 +			if (rs != null) {
  1.2525 +				try {
  1.2526 +					rs.close();
  1.2527 +				} catch (SQLException ex) {
  1.2528 +					ex.printStackTrace();
  1.2529 +				}
  1.2530 +			}
  1.2531 +		}
  1.2532 +	}
  1.2533 +
  1.2534 +	@Override
  1.2535 +	public List<String> getListsForGroup(String group)
  1.2536 +		throws StorageBackendException
  1.2537 +	{
  1.2538 +		ResultSet rs = null;
  1.2539 +		List<String> lists = new ArrayList<String>();
  1.2540 +
  1.2541 +		try {
  1.2542 +			this.pstmtGetListForGroup.setString(1, group);
  1.2543 +			rs = this.pstmtGetListForGroup.executeQuery();
  1.2544 +
  1.2545 +			while (rs.next()) {
  1.2546 +				lists.add(rs.getString(1));
  1.2547 +			}
  1.2548 +			return lists;
  1.2549 +		} catch (SQLException ex) {
  1.2550 +			restartConnection(ex);
  1.2551 +			return getListsForGroup(group);
  1.2552 +		} finally {
  1.2553 +			if (rs != null) {
  1.2554 +				try {
  1.2555 +					rs.close();
  1.2556 +				} catch (SQLException ex) {
  1.2557 +					ex.printStackTrace();
  1.2558 +				}
  1.2559 +			}
  1.2560 +		}
  1.2561 +	}
  1.2562 +
  1.2563 +	private int getMaxArticleIndex(long groupID)
  1.2564 +		throws StorageBackendException
  1.2565 +	{
  1.2566 +		ResultSet rs = null;
  1.2567 +
  1.2568 +		try {
  1.2569 +			this.pstmtGetMaxArticleIndex.setLong(1, groupID);
  1.2570 +			rs = this.pstmtGetMaxArticleIndex.executeQuery();
  1.2571 +
  1.2572 +			int maxIndex = 0;
  1.2573 +			if (rs.next()) {
  1.2574 +				maxIndex = rs.getInt(1);
  1.2575 +			}
  1.2576 +
  1.2577 +			return maxIndex;
  1.2578 +		} catch (SQLException ex) {
  1.2579 +			restartConnection(ex);
  1.2580 +			return getMaxArticleIndex(groupID);
  1.2581 +		} finally {
  1.2582 +			if (rs != null) {
  1.2583 +				try {
  1.2584 +					rs.close();
  1.2585 +				} catch (SQLException ex) {
  1.2586 +					ex.printStackTrace();
  1.2587 +				}
  1.2588 +			}
  1.2589 +		}
  1.2590 +	}
  1.2591 +
  1.2592 +	private int getMaxArticleID()
  1.2593 +		throws StorageBackendException
  1.2594 +	{
  1.2595 +		ResultSet rs = null;
  1.2596 +
  1.2597 +		try {
  1.2598 +			rs = this.pstmtGetMaxArticleID.executeQuery();
  1.2599 +
  1.2600 +			int maxIndex = 0;
  1.2601 +			if (rs.next()) {
  1.2602 +				maxIndex = rs.getInt(1);
  1.2603 +			}
  1.2604 +
  1.2605 +			return maxIndex;
  1.2606 +		} catch (SQLException ex) {
  1.2607 +			restartConnection(ex);
  1.2608 +			return getMaxArticleID();
  1.2609 +		} finally {
  1.2610 +			if (rs != null) {
  1.2611 +				try {
  1.2612 +					rs.close();
  1.2613 +				} catch (SQLException ex) {
  1.2614 +					ex.printStackTrace();
  1.2615 +				}
  1.2616 +			}
  1.2617 +		}
  1.2618 +	}
  1.2619 +
  1.2620 +	@Override
  1.2621 +	public int getLastArticleNumber(Group group)
  1.2622 +		throws StorageBackendException
  1.2623 +	{
  1.2624 +		ResultSet rs = null;
  1.2625 +
  1.2626 +		try {
  1.2627 +			this.pstmtGetLastArticleNumber.setLong(1, group.getInternalID());
  1.2628 +			rs = this.pstmtGetLastArticleNumber.executeQuery();
  1.2629 +			if (rs.next()) {
  1.2630 +				return rs.getInt(1);
  1.2631 +			} else {
  1.2632 +				return 0;
  1.2633 +			}
  1.2634 +		} catch (SQLException ex) {
  1.2635 +			restartConnection(ex);
  1.2636 +			return getLastArticleNumber(group);
  1.2637 +		} finally {
  1.2638 +			if (rs != null) {
  1.2639 +				try {
  1.2640 +					rs.close();
  1.2641 +				} catch (SQLException ex) {
  1.2642 +					ex.printStackTrace();
  1.2643 +				}
  1.2644 +			}
  1.2645 +		}
  1.2646 +	}
  1.2647 +
  1.2648 +	@Override
  1.2649 +	public int getFirstArticleNumber(Group group)
  1.2650 +		throws StorageBackendException
  1.2651 +	{
  1.2652 +		ResultSet rs = null;
  1.2653 +		try {
  1.2654 +			this.pstmtGetFirstArticleNumber.setLong(1, group.getInternalID());
  1.2655 +			rs = this.pstmtGetFirstArticleNumber.executeQuery();
  1.2656 +			if (rs.next()) {
  1.2657 +				return rs.getInt(1);
  1.2658 +			} else {
  1.2659 +				return 0;
  1.2660 +			}
  1.2661 +		} catch (SQLException ex) {
  1.2662 +			restartConnection(ex);
  1.2663 +			return getFirstArticleNumber(group);
  1.2664 +		} finally {
  1.2665 +			if (rs != null) {
  1.2666 +				try {
  1.2667 +					rs.close();
  1.2668 +				} catch (SQLException ex) {
  1.2669 +					ex.printStackTrace();
  1.2670 +				}
  1.2671 +			}
  1.2672 +		}
  1.2673 +	}
  1.2674 +
  1.2675 +	/**
  1.2676 +	 * Returns a group name identified by the given id.
  1.2677 +	 * @param id
  1.2678 +	 * @return
  1.2679 +	 * @throws StorageBackendException
  1.2680 +	 */
  1.2681 +	public String getGroup(int id)
  1.2682 +		throws StorageBackendException
  1.2683 +	{
  1.2684 +		ResultSet rs = null;
  1.2685 +
  1.2686 +		try {
  1.2687 +			this.pstmtGetGroup1.setInt(1, id);
  1.2688 +			rs = this.pstmtGetGroup1.executeQuery();
  1.2689 +
  1.2690 +			if (rs.next()) {
  1.2691 +				return rs.getString(1);
  1.2692 +			} else {
  1.2693 +				return null;
  1.2694 +			}
  1.2695 +		} catch (SQLException ex) {
  1.2696 +			restartConnection(ex);
  1.2697 +			return getGroup(id);
  1.2698 +		} finally {
  1.2699 +			if (rs != null) {
  1.2700 +				try {
  1.2701 +					rs.close();
  1.2702 +				} catch (SQLException ex) {
  1.2703 +					ex.printStackTrace();
  1.2704 +				}
  1.2705 +			}
  1.2706 +		}
  1.2707 +	}
  1.2708 +
  1.2709 +	@Override
  1.2710 +	public double getEventsPerHour(int key, long gid)
  1.2711 +		throws StorageBackendException
  1.2712 +	{
  1.2713 +		String gidquery = "";
  1.2714 +		if (gid >= 0) {
  1.2715 +			gidquery = " AND group_id = " + gid;
  1.2716 +		}
  1.2717 +
  1.2718 +		Statement stmt = null;
  1.2719 +		ResultSet rs = null;
  1.2720 +
  1.2721 +		try {
  1.2722 +			stmt = this.conn.createStatement();
  1.2723 +			rs = stmt.executeQuery("SELECT Count(*) / (Max(event_time) - Min(event_time))"
  1.2724 +				+ " * 1000 * 60 * 60 FROM events WHERE event_key = " + key + gidquery);
  1.2725 +
  1.2726 +			if (rs.next()) {
  1.2727 +				restarts = 0; // reset error count
  1.2728 +				return rs.getDouble(1);
  1.2729 +			} else {
  1.2730 +				return Double.NaN;
  1.2731 +			}
  1.2732 +		} catch (SQLException ex) {
  1.2733 +			restartConnection(ex);
  1.2734 +			return getEventsPerHour(key, gid);
  1.2735 +		} finally {
  1.2736 +			try {
  1.2737 +				if (stmt != null) {
  1.2738 +					stmt.close(); // Implicitely closes the result sets
  1.2739 +				}
  1.2740 +			} catch (SQLException ex) {
  1.2741 +				ex.printStackTrace();
  1.2742 +			}
  1.2743 +		}
  1.2744 +	}
  1.2745 +
  1.2746 +	@Override
  1.2747 +	public String getOldestArticle()
  1.2748 +		throws StorageBackendException
  1.2749 +	{
  1.2750 +		ResultSet rs = null;
  1.2751 +
  1.2752 +		try {
  1.2753 +			rs = this.pstmtGetOldestArticle.executeQuery();
  1.2754 +			if (rs.next()) {
  1.2755 +				return rs.getString(1);
  1.2756 +			} else {
  1.2757 +				return null;
  1.2758 +			}
  1.2759 +		} catch (SQLException ex) {
  1.2760 +			restartConnection(ex);
  1.2761 +			return getOldestArticle();
  1.2762 +		} finally {
  1.2763 +			if (rs != null) {
  1.2764 +				try {
  1.2765 +					rs.close();
  1.2766 +				} catch (SQLException ex) {
  1.2767 +					ex.printStackTrace();
  1.2768 +				}
  1.2769 +			}
  1.2770 +		}
  1.2771 +	}
  1.2772 +
  1.2773 +	@Override
  1.2774 +	public int getPostingsCount(String groupname)
  1.2775 +		throws StorageBackendException
  1.2776 +	{
  1.2777 +		ResultSet rs = null;
  1.2778 +
  1.2779 +		try {
  1.2780 +			this.pstmtGetPostingsCount.setString(1, groupname);
  1.2781 +			rs = this.pstmtGetPostingsCount.executeQuery();
  1.2782 +			if (rs.next()) {
  1.2783 +				return rs.getInt(1);
  1.2784 +			} else {
  1.2785 +				Log.get().warning("Count on postings return nothing!");
  1.2786 +				return 0;
  1.2787 +			}
  1.2788 +		} catch (SQLException ex) {
  1.2789 +			restartConnection(ex);
  1.2790 +			return getPostingsCount(groupname);
  1.2791 +		} finally {
  1.2792 +			if (rs != null) {
  1.2793 +				try {
  1.2794 +					rs.close();
  1.2795 +				} catch (SQLException ex) {
  1.2796 +					ex.printStackTrace();
  1.2797 +				}
  1.2798 +			}
  1.2799 +		}
  1.2800 +	}
  1.2801 +
  1.2802 +	@Override
  1.2803 +	public List<Subscription> getSubscriptions(int feedtype)
  1.2804 +		throws StorageBackendException
  1.2805 +	{
  1.2806 +		ResultSet rs = null;
  1.2807 +
  1.2808 +		try {
  1.2809 +			List<Subscription> subs = new ArrayList<Subscription>();
  1.2810 +			this.pstmtGetSubscriptions.setInt(1, feedtype);
  1.2811 +			rs = this.pstmtGetSubscriptions.executeQuery();
  1.2812 +
  1.2813 +			while (rs.next()) {
  1.2814 +				String host = rs.getString("host");
  1.2815 +				String group = rs.getString("name");
  1.2816 +				int port = rs.getInt("port");
  1.2817 +				subs.add(new Subscription(host, port, feedtype, group));
  1.2818 +			}
  1.2819 +
  1.2820 +			return subs;
  1.2821 +		} catch (SQLException ex) {
  1.2822 +			restartConnection(ex);
  1.2823 +			return getSubscriptions(feedtype);
  1.2824 +		} finally {
  1.2825 +			if (rs != null) {
  1.2826 +				try {
  1.2827 +					rs.close();
  1.2828 +				} catch (SQLException ex) {
  1.2829 +					ex.printStackTrace();
  1.2830 +				}
  1.2831 +			}
  1.2832 +		}
  1.2833 +	}
  1.2834 +
  1.2835 +	/**
  1.2836 +	 * Checks if there is an article with the given messageid in the JDBCDatabase.
  1.2837 +	 * @param name
  1.2838 +	 * @return
  1.2839 +	 * @throws StorageBackendException
  1.2840 +	 */
  1.2841 +	@Override
  1.2842 +	public boolean isArticleExisting(String messageID)
  1.2843 +		throws StorageBackendException
  1.2844 +	{
  1.2845 +		ResultSet rs = null;
  1.2846 +
  1.2847 +		try {
  1.2848 +			this.pstmtIsArticleExisting.setString(1, messageID);
  1.2849 +			rs = this.pstmtIsArticleExisting.executeQuery();
  1.2850 +			return rs.next() && rs.getInt(1) == 1;
  1.2851 +		} catch (SQLException ex) {
  1.2852 +			restartConnection(ex);
  1.2853 +			return isArticleExisting(messageID);
  1.2854 +		} finally {
  1.2855 +			if (rs != null) {
  1.2856 +				try {
  1.2857 +					rs.close();
  1.2858 +				} catch (SQLException ex) {
  1.2859 +					ex.printStackTrace();
  1.2860 +				}
  1.2861 +			}
  1.2862 +		}
  1.2863 +	}
  1.2864 +
  1.2865 +	/**
  1.2866 +	 * Checks if there is a group with the given name in the JDBCDatabase.
  1.2867 +	 * @param name
  1.2868 +	 * @return
  1.2869 +	 * @throws StorageBackendException
  1.2870 +	 */
  1.2871 +	@Override
  1.2872 +	public boolean isGroupExisting(String name)
  1.2873 +		throws StorageBackendException
  1.2874 +	{
  1.2875 +		ResultSet rs = null;
  1.2876 +
  1.2877 +		try {
  1.2878 +			this.pstmtIsGroupExisting.setString(1, name);
  1.2879 +			rs = this.pstmtIsGroupExisting.executeQuery();
  1.2880 +			return rs.next();
  1.2881 +		} catch (SQLException ex) {
  1.2882 +			restartConnection(ex);
  1.2883 +			return isGroupExisting(name);
  1.2884 +		} finally {
  1.2885 +			if (rs != null) {
  1.2886 +				try {
  1.2887 +					rs.close();
  1.2888 +				} catch (SQLException ex) {
  1.2889 +					ex.printStackTrace();
  1.2890 +				}
  1.2891 +			}
  1.2892 +		}
  1.2893 +	}
  1.2894 +
  1.2895 +	@Override
  1.2896 +	public void setConfigValue(String key, String value)
  1.2897 +		throws StorageBackendException
  1.2898 +	{
  1.2899 +		try {
  1.2900 +			conn.setAutoCommit(false);
  1.2901 +			this.pstmtSetConfigValue0.setString(1, key);
  1.2902 +			this.pstmtSetConfigValue0.execute();
  1.2903 +			this.pstmtSetConfigValue1.setString(1, key);
  1.2904 +			this.pstmtSetConfigValue1.setString(2, value);
  1.2905 +			this.pstmtSetConfigValue1.execute();
  1.2906 +			conn.commit();
  1.2907 +			conn.setAutoCommit(true);
  1.2908 +		} catch (SQLException ex) {
  1.2909 +			restartConnection(ex);
  1.2910 +			setConfigValue(key, value);
  1.2911 +		}
  1.2912 +	}
  1.2913 +
  1.2914 +	/**
  1.2915 +	 * Closes the JDBCDatabase connection.
  1.2916 +	 */
  1.2917 +	public void shutdown()
  1.2918 +		throws StorageBackendException
  1.2919 +	{
  1.2920 +		try {
  1.2921 +			if (this.conn != null) {
  1.2922 +				this.conn.close();
  1.2923 +			}
  1.2924 +		} catch (SQLException ex) {
  1.2925 +			throw new StorageBackendException(ex);
  1.2926 +		}
  1.2927 +	}
  1.2928 +
  1.2929 +	@Override
  1.2930 +	public void purgeGroup(Group group)
  1.2931 +		throws StorageBackendException
  1.2932 +	{
  1.2933 +		try {
  1.2934 +			this.pstmtPurgeGroup0.setLong(1, group.getInternalID());
  1.2935 +			this.pstmtPurgeGroup0.executeUpdate();
  1.2936 +
  1.2937 +			this.pstmtPurgeGroup1.setLong(1, group.getInternalID());
  1.2938 +			this.pstmtPurgeGroup1.executeUpdate();
  1.2939 +		} catch (SQLException ex) {
  1.2940 +			restartConnection(ex);
  1.2941 +			purgeGroup(group);
  1.2942 +		}
  1.2943 +	}
  1.2944 +
  1.2945 +	private void restartConnection(SQLException cause)
  1.2946 +		throws StorageBackendException
  1.2947 +	{
  1.2948 +		restarts++;
  1.2949 +		Log.get().severe(Thread.currentThread()
  1.2950 +			+ ": Database connection was closed (restart " + restarts + ").");
  1.2951 +
  1.2952 +		if (restarts >= MAX_RESTARTS) {
  1.2953 +			// Delete the current, probably broken JDBCDatabase instance.
  1.2954 +			// So no one can use the instance any more.
  1.2955 +			JDBCDatabaseProvider.instances.remove(Thread.currentThread());
  1.2956 +
  1.2957 +			// Throw the exception upwards
  1.2958 +			throw new StorageBackendException(cause);
  1.2959 +		}
  1.2960 +
  1.2961 +		try {
  1.2962 +			Thread.sleep(1500L * restarts);
  1.2963 +		} catch (InterruptedException ex) {
  1.2964 +			Log.get().warning("Interrupted: " + ex.getMessage());
  1.2965 +		}
  1.2966 +
  1.2967 +		// Try to properly close the old database connection
  1.2968 +		try {
  1.2969 +			if (this.conn != null) {
  1.2970 +				this.conn.close();
  1.2971 +			}
  1.2972 +		} catch (SQLException ex) {
  1.2973 +			Log.get().warning(ex.getMessage());
  1.2974 +		}
  1.2975 +
  1.2976 +		try {
  1.2977 +			// Try to reinitialize database connection
  1.2978 +			arise();
  1.2979 +		} catch (SQLException ex) {
  1.2980 +			Log.get().warning(ex.getMessage());
  1.2981 +			restartConnection(ex);
  1.2982 +		}
  1.2983 +	}
  1.2984 +
  1.2985 +	@Override
  1.2986 +	public boolean update(Article article)
  1.2987 +		throws StorageBackendException
  1.2988 +	{
  1.2989 +		// DELETE FROM headers WHERE article_id = ?
  1.2990 +
  1.2991 +		// INSERT INTO headers ...
  1.2992 +
  1.2993 +		// SELECT * FROM postings WHERE article_id = ? AND group_id = ?
  1.2994 +		return false;
  1.2995 +	}
  1.2996 +
  1.2997 +	/**
  1.2998 +	 * Writes the flags and the name of the given group to the database.
  1.2999 +	 * @param group
  1.3000 +	 * @throws StorageBackendException
  1.3001 +	 */
  1.3002 +	@Override
  1.3003 +	public boolean update(Group group)
  1.3004 +		throws StorageBackendException
  1.3005 +	{
  1.3006 +		try {
  1.3007 +			this.pstmtUpdateGroup.setInt(1, group.getFlags());
  1.3008 +			this.pstmtUpdateGroup.setString(2, group.getName());
  1.3009 +			this.pstmtUpdateGroup.setLong(3, group.getInternalID());
  1.3010 +			int rs = this.pstmtUpdateGroup.executeUpdate();
  1.3011 +			return rs == 1;
  1.3012 +		} catch (SQLException ex) {
  1.3013 +			restartConnection(ex);
  1.3014 +			return update(group);
  1.3015 +		}
  1.3016 +	}
  1.3017  }