org/sonews/storage/impl/JDBCDatabase.java
author cli
Thu Aug 20 14:31:19 2009 +0200 (2009-08-20)
changeset 12 bb6990c0dd1a
parent 3 2fdc9cc89502
child 14 efce4ec25564
permissions -rw-r--r--
Merging fixes from sonews/1.0.3
chris@3
     1
/*
chris@3
     2
 *   SONEWS News Server
chris@3
     3
 *   see AUTHORS for the list of contributors
chris@3
     4
 *
chris@3
     5
 *   This program is free software: you can redistribute it and/or modify
chris@3
     6
 *   it under the terms of the GNU General Public License as published by
chris@3
     7
 *   the Free Software Foundation, either version 3 of the License, or
chris@3
     8
 *   (at your option) any later version.
chris@3
     9
 *
chris@3
    10
 *   This program is distributed in the hope that it will be useful,
chris@3
    11
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
chris@3
    12
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
chris@3
    13
 *   GNU General Public License for more details.
chris@3
    14
 *
chris@3
    15
 *   You should have received a copy of the GNU General Public License
chris@3
    16
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
chris@3
    17
 */
chris@3
    18
chris@3
    19
package org.sonews.storage.impl;
chris@3
    20
chris@3
    21
import java.sql.Connection;
chris@3
    22
import java.sql.DriverManager;
chris@3
    23
import java.sql.ResultSet;
chris@3
    24
import java.sql.SQLException;
chris@3
    25
import java.sql.Statement;
chris@3
    26
import java.sql.PreparedStatement;
chris@3
    27
import java.util.ArrayList;
chris@3
    28
import java.util.Enumeration;
chris@3
    29
import java.util.List;
chris@3
    30
import java.util.regex.Matcher;
chris@3
    31
import java.util.regex.Pattern;
chris@3
    32
import java.util.regex.PatternSyntaxException;
chris@3
    33
import javax.mail.Header;
chris@3
    34
import javax.mail.internet.InternetAddress;
chris@3
    35
import javax.mail.internet.MimeUtility;
chris@3
    36
import org.sonews.config.Config;
chris@3
    37
import org.sonews.util.Log;
chris@3
    38
import org.sonews.feed.Subscription;
chris@3
    39
import org.sonews.storage.Article;
chris@3
    40
import org.sonews.storage.ArticleHead;
chris@3
    41
import org.sonews.storage.Channel;
chris@3
    42
import org.sonews.storage.Group;
chris@3
    43
import org.sonews.storage.Storage;
chris@3
    44
import org.sonews.storage.StorageBackendException;
chris@3
    45
import org.sonews.util.Pair;
chris@3
    46
chris@3
    47
/**
chris@3
    48
 * JDBCDatabase facade class.
chris@3
    49
 * @author Christian Lins
chris@3
    50
 * @since sonews/0.5.0
chris@3
    51
 */
chris@3
    52
// TODO: Refactor this class to reduce size (e.g. ArticleDatabase GroupDatabase)
chris@3
    53
public class JDBCDatabase implements Storage
chris@3
    54
{
chris@3
    55
chris@3
    56
  public static final int MAX_RESTARTS = 3;
chris@3
    57
  
chris@3
    58
  private Connection        conn = null;
chris@3
    59
  private PreparedStatement pstmtAddArticle1 = null;
chris@3
    60
  private PreparedStatement pstmtAddArticle2 = null;
chris@3
    61
  private PreparedStatement pstmtAddArticle3 = null;
chris@3
    62
  private PreparedStatement pstmtAddArticle4 = null;
chris@3
    63
  private PreparedStatement pstmtAddGroup0   = null;
chris@3
    64
  private PreparedStatement pstmtAddEvent = null;
chris@3
    65
  private PreparedStatement pstmtCountArticles = null;
chris@3
    66
  private PreparedStatement pstmtCountGroups   = null;
chris@3
    67
  private PreparedStatement pstmtDeleteArticle0 = null;
chris@3
    68
  private PreparedStatement pstmtDeleteArticle1 = null;
chris@3
    69
  private PreparedStatement pstmtDeleteArticle2 = null;
chris@3
    70
  private PreparedStatement pstmtDeleteArticle3 = null;
chris@3
    71
  private PreparedStatement pstmtGetArticle0 = null;
chris@3
    72
  private PreparedStatement pstmtGetArticle1 = null;
chris@3
    73
  private PreparedStatement pstmtGetArticleHeaders0 = null;
chris@3
    74
  private PreparedStatement pstmtGetArticleHeaders1 = null;
chris@3
    75
  private PreparedStatement pstmtGetArticleHeads = null;
chris@3
    76
  private PreparedStatement pstmtGetArticleIDs   = null;
chris@3
    77
  private PreparedStatement pstmtGetArticleIndex    = null;
chris@3
    78
  private PreparedStatement pstmtGetConfigValue = null;
chris@3
    79
  private PreparedStatement pstmtGetEventsCount0 = null;
chris@3
    80
  private PreparedStatement pstmtGetEventsCount1 = null;
chris@3
    81
  private PreparedStatement pstmtGetGroupForList = null;
chris@3
    82
  private PreparedStatement pstmtGetGroup0     = null;
chris@3
    83
  private PreparedStatement pstmtGetGroup1     = null;
chris@3
    84
  private PreparedStatement pstmtGetFirstArticleNumber = null;
chris@3
    85
  private PreparedStatement pstmtGetListForGroup       = null;
chris@3
    86
  private PreparedStatement pstmtGetLastArticleNumber  = null;
chris@3
    87
  private PreparedStatement pstmtGetMaxArticleID       = null;
chris@3
    88
  private PreparedStatement pstmtGetMaxArticleIndex    = null;
chris@3
    89
  private PreparedStatement pstmtGetOldestArticle      = null;
chris@3
    90
  private PreparedStatement pstmtGetPostingsCount      = null;
chris@3
    91
  private PreparedStatement pstmtGetSubscriptions  = null;
chris@3
    92
  private PreparedStatement pstmtIsArticleExisting = null;
chris@3
    93
  private PreparedStatement pstmtIsGroupExisting = null;
chris@3
    94
  private PreparedStatement pstmtPurgeGroup0     = null;
chris@3
    95
  private PreparedStatement pstmtPurgeGroup1     = null;
chris@3
    96
  private PreparedStatement pstmtSetConfigValue0 = null;
chris@3
    97
  private PreparedStatement pstmtSetConfigValue1 = null;
chris@3
    98
  private PreparedStatement pstmtUpdateGroup     = null;
chris@3
    99
  
chris@3
   100
  /** How many times the database connection was reinitialized */
chris@3
   101
  private int restarts = 0;
chris@3
   102
  
chris@3
   103
  /**
chris@3
   104
   * Rises the database: reconnect and recreate all prepared statements.
chris@3
   105
   * @throws java.lang.SQLException
chris@3
   106
   */
chris@3
   107
  protected void arise()
chris@3
   108
    throws SQLException
chris@3
   109
  {
chris@3
   110
    try
chris@3
   111
    {
chris@3
   112
      // Load database driver
chris@3
   113
      Class.forName(
chris@3
   114
              Config.inst().get(Config.STORAGE_DBMSDRIVER, "java.lang.Object"));
chris@3
   115
chris@3
   116
      // Establish database connection
chris@3
   117
      this.conn = DriverManager.getConnection(
chris@3
   118
              Config.inst().get(Config.STORAGE_DATABASE, "<not specified>"),
chris@3
   119
              Config.inst().get(Config.STORAGE_USER, "root"),
chris@3
   120
              Config.inst().get(Config.STORAGE_PASSWORD, ""));
chris@3
   121
chris@3
   122
      this.conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
chris@3
   123
      if(this.conn.getTransactionIsolation() != Connection.TRANSACTION_SERIALIZABLE)
chris@3
   124
      {
chris@3
   125
        Log.msg("Warning: Database is NOT fully serializable!", false);
chris@3
   126
      }
chris@3
   127
chris@3
   128
      // Prepare statements for method addArticle()
chris@3
   129
      this.pstmtAddArticle1 = conn.prepareStatement(
chris@3
   130
        "INSERT INTO articles (article_id, body) VALUES(?, ?)");
chris@3
   131
      this.pstmtAddArticle2 = conn.prepareStatement(
chris@3
   132
        "INSERT INTO headers (article_id, header_key, header_value, header_index) " +
chris@3
   133
        "VALUES (?, ?, ?, ?)");
chris@3
   134
      this.pstmtAddArticle3 = conn.prepareStatement(
chris@3
   135
        "INSERT INTO postings (group_id, article_id, article_index)" +
chris@3
   136
        "VALUES (?, ?, ?)");
chris@3
   137
      this.pstmtAddArticle4 = conn.prepareStatement(
chris@3
   138
        "INSERT INTO article_ids (article_id, message_id) VALUES (?, ?)");
chris@3
   139
chris@3
   140
      // Prepare statement for method addStatValue()
chris@3
   141
      this.pstmtAddEvent = conn.prepareStatement(
chris@3
   142
        "INSERT INTO events VALUES (?, ?, ?)");
chris@3
   143
     
chris@3
   144
      // Prepare statement for method addGroup()
chris@3
   145
      this.pstmtAddGroup0 = conn.prepareStatement(
chris@3
   146
        "INSERT INTO groups (name, flags) VALUES (?, ?)");
chris@3
   147
      
chris@3
   148
      // Prepare statement for method countArticles()
chris@3
   149
      this.pstmtCountArticles = conn.prepareStatement(
chris@3
   150
        "SELECT Count(article_id) FROM article_ids");
chris@3
   151
      
chris@3
   152
      // Prepare statement for method countGroups()
chris@3
   153
      this.pstmtCountGroups = conn.prepareStatement(
chris@3
   154
        "SELECT Count(group_id) FROM groups WHERE " +
chris@3
   155
        "flags & " + Channel.DELETED + " = 0");
chris@3
   156
      
chris@3
   157
      // Prepare statements for method delete(article)
chris@3
   158
      this.pstmtDeleteArticle0 = conn.prepareStatement(
chris@3
   159
        "DELETE FROM articles WHERE article_id = " +
chris@3
   160
        "(SELECT article_id FROM article_ids WHERE message_id = ?)");
chris@3
   161
      this.pstmtDeleteArticle1 = conn.prepareStatement(
chris@3
   162
        "DELETE FROM headers WHERE article_id = " +
chris@3
   163
        "(SELECT article_id FROM article_ids WHERE message_id = ?)");
chris@3
   164
      this.pstmtDeleteArticle2 = conn.prepareStatement(
chris@3
   165
        "DELETE FROM postings WHERE article_id = " +
chris@3
   166
        "(SELECT article_id FROM article_ids WHERE message_id = ?)");
chris@3
   167
      this.pstmtDeleteArticle3 = conn.prepareStatement(
chris@3
   168
        "DELETE FROM article_ids WHERE message_id = ?");
chris@3
   169
chris@3
   170
      // Prepare statements for methods getArticle()
chris@3
   171
      this.pstmtGetArticle0 = conn.prepareStatement(
chris@3
   172
        "SELECT * FROM articles  WHERE article_id = " +
chris@3
   173
        "(SELECT article_id FROM article_ids WHERE message_id = ?)");
chris@3
   174
      this.pstmtGetArticle1 = conn.prepareStatement(
chris@3
   175
        "SELECT * FROM articles WHERE article_id = " +
chris@3
   176
        "(SELECT article_id FROM postings WHERE " +
chris@3
   177
        "article_index = ? AND group_id = ?)");
chris@3
   178
      
chris@3
   179
      // Prepare statement for method getArticleHeaders()
chris@3
   180
      this.pstmtGetArticleHeaders0 = conn.prepareStatement(
chris@3
   181
        "SELECT header_key, header_value FROM headers WHERE article_id = ? " +
chris@3
   182
        "ORDER BY header_index ASC");
chris@3
   183
chris@3
   184
      // Prepare statement for method getArticleHeaders(regular expr pattern)
chris@3
   185
      this.pstmtGetArticleHeaders1 = conn.prepareStatement(
chris@3
   186
        "SELECT p.article_index, h.header_value FROM headers h " +
chris@3
   187
          "INNER JOIN postings p ON h.article_id = p.article_id " +
chris@3
   188
          "INNER JOIN groups g ON p.group_id = g.group_id " +
chris@3
   189
            "WHERE g.name          =  ? AND " +
chris@3
   190
                  "h.header_key    =  ? AND " +
chris@3
   191
                  "p.article_index >= ? " +
chris@3
   192
        "ORDER BY p.article_index ASC");
chris@3
   193
chris@3
   194
      this.pstmtGetArticleIDs = conn.prepareStatement(
chris@3
   195
        "SELECT article_index FROM postings WHERE group_id = ?");
chris@3
   196
      
chris@3
   197
      // Prepare statement for method getArticleIndex
chris@3
   198
      this.pstmtGetArticleIndex = conn.prepareStatement(
chris@3
   199
              "SELECT article_index FROM postings WHERE " +
chris@3
   200
              "article_id = (SELECT article_id FROM article_ids " +
chris@3
   201
              "WHERE message_id = ?) " +
chris@3
   202
              " AND group_id = ?");
chris@3
   203
chris@3
   204
      // Prepare statements for method getArticleHeads()
chris@3
   205
      this.pstmtGetArticleHeads = conn.prepareStatement(
chris@3
   206
        "SELECT article_id, article_index FROM postings WHERE " +
chris@3
   207
        "postings.group_id = ? AND article_index >= ? AND " +
chris@3
   208
        "article_index <= ?");
chris@3
   209
chris@3
   210
      // Prepare statements for method getConfigValue()
chris@3
   211
      this.pstmtGetConfigValue = conn.prepareStatement(
chris@3
   212
        "SELECT config_value FROM config WHERE config_key = ?");
chris@3
   213
chris@3
   214
      // Prepare statements for method getEventsCount()
chris@3
   215
      this.pstmtGetEventsCount0 = conn.prepareStatement(
chris@3
   216
        "SELECT Count(*) FROM events WHERE event_key = ? AND " +
chris@3
   217
        "event_time >= ? AND event_time < ?");
chris@3
   218
chris@3
   219
      this.pstmtGetEventsCount1 = conn.prepareStatement(
chris@3
   220
        "SELECT Count(*) FROM events WHERE event_key = ? AND " +
chris@3
   221
        "event_time >= ? AND event_time < ? AND group_id = ?");
chris@3
   222
      
chris@3
   223
      // Prepare statement for method getGroupForList()
chris@3
   224
      this.pstmtGetGroupForList = conn.prepareStatement(
chris@3
   225
        "SELECT name FROM groups INNER JOIN groups2list " +
chris@3
   226
        "ON groups.group_id = groups2list.group_id " +
chris@3
   227
        "WHERE groups2list.listaddress = ?");
chris@3
   228
chris@3
   229
      // Prepare statement for method getGroup()
chris@3
   230
      this.pstmtGetGroup0 = conn.prepareStatement(
chris@3
   231
        "SELECT group_id, flags FROM groups WHERE Name = ?");
chris@3
   232
      this.pstmtGetGroup1 = conn.prepareStatement(
chris@3
   233
        "SELECT name FROM groups WHERE group_id = ?");
chris@3
   234
chris@3
   235
      // Prepare statement for method getLastArticleNumber()
chris@3
   236
      this.pstmtGetLastArticleNumber = conn.prepareStatement(
chris@3
   237
        "SELECT Max(article_index) FROM postings WHERE group_id = ?");
chris@3
   238
chris@3
   239
      // Prepare statement for method getListForGroup()
chris@3
   240
      this.pstmtGetListForGroup = conn.prepareStatement(
chris@3
   241
        "SELECT listaddress FROM groups2list INNER JOIN groups " +
chris@3
   242
        "ON groups.group_id = groups2list.group_id WHERE name = ?");
chris@3
   243
chris@3
   244
      // Prepare statement for method getMaxArticleID()
chris@3
   245
      this.pstmtGetMaxArticleID = conn.prepareStatement(
chris@3
   246
        "SELECT Max(article_id) FROM articles");
chris@3
   247
      
chris@3
   248
      // Prepare statement for method getMaxArticleIndex()
chris@3
   249
      this.pstmtGetMaxArticleIndex = conn.prepareStatement(
chris@3
   250
        "SELECT Max(article_index) FROM postings WHERE group_id = ?");
chris@3
   251
      
chris@3
   252
      // Prepare statement for method getOldestArticle()
chris@3
   253
      this.pstmtGetOldestArticle = conn.prepareStatement(
chris@3
   254
        "SELECT message_id FROM article_ids WHERE article_id = " +
chris@3
   255
        "(SELECT Min(article_id) FROM article_ids)");
chris@3
   256
chris@3
   257
      // Prepare statement for method getFirstArticleNumber()
chris@3
   258
      this.pstmtGetFirstArticleNumber = conn.prepareStatement(
chris@3
   259
        "SELECT Min(article_index) FROM postings WHERE group_id = ?");
chris@3
   260
      
chris@3
   261
      // Prepare statement for method getPostingsCount()
chris@3
   262
      this.pstmtGetPostingsCount = conn.prepareStatement(
chris@3
   263
        "SELECT Count(*) FROM postings NATURAL JOIN groups " +
chris@3
   264
        "WHERE groups.name = ?");
chris@3
   265
      
chris@3
   266
      // Prepare statement for method getSubscriptions()
chris@3
   267
      this.pstmtGetSubscriptions = conn.prepareStatement(
chris@3
   268
        "SELECT host, port, name FROM peers NATURAL JOIN " +
chris@3
   269
        "peer_subscriptions NATURAL JOIN groups WHERE feedtype = ?");
chris@3
   270
      
chris@3
   271
      // Prepare statement for method isArticleExisting()
chris@3
   272
      this.pstmtIsArticleExisting = conn.prepareStatement(
chris@3
   273
        "SELECT Count(article_id) FROM article_ids WHERE message_id = ?");
chris@3
   274
      
chris@3
   275
      // Prepare statement for method isGroupExisting()
chris@3
   276
      this.pstmtIsGroupExisting = conn.prepareStatement(
chris@3
   277
        "SELECT * FROM groups WHERE name = ?");
chris@3
   278
      
chris@3
   279
      // Prepare statement for method setConfigValue()
chris@3
   280
      this.pstmtSetConfigValue0 = conn.prepareStatement(
chris@3
   281
        "DELETE FROM config WHERE config_key = ?");
chris@3
   282
      this.pstmtSetConfigValue1 = conn.prepareStatement(
chris@3
   283
        "INSERT INTO config VALUES(?, ?)");
chris@3
   284
chris@3
   285
      // Prepare statements for method purgeGroup()
chris@3
   286
      this.pstmtPurgeGroup0 = conn.prepareStatement(
chris@3
   287
        "DELETE FROM peer_subscriptions WHERE group_id = ?");
chris@3
   288
      this.pstmtPurgeGroup1 = conn.prepareStatement(
chris@3
   289
        "DELETE FROM groups WHERE group_id = ?");
chris@3
   290
chris@3
   291
      // Prepare statement for method update(Group)
chris@3
   292
      this.pstmtUpdateGroup = conn.prepareStatement(
chris@3
   293
        "UPDATE groups SET flags = ?, name = ? WHERE group_id = ?");
chris@3
   294
    }
chris@3
   295
    catch(ClassNotFoundException ex)
chris@3
   296
    {
chris@3
   297
      throw new Error("JDBC Driver not found!", ex);
chris@3
   298
    }
chris@3
   299
  }
chris@3
   300
  
chris@3
   301
  /**
chris@3
   302
   * Adds an article to the database.
chris@3
   303
   * @param article
chris@3
   304
   * @return
chris@3
   305
   * @throws java.sql.SQLException
chris@3
   306
   */
chris@3
   307
  @Override
chris@3
   308
  public void addArticle(final Article article)
chris@3
   309
    throws StorageBackendException
chris@3
   310
  {
chris@3
   311
    try
chris@3
   312
    {
chris@3
   313
      this.conn.setAutoCommit(false);
chris@3
   314
chris@3
   315
      int newArticleID = getMaxArticleID() + 1;
chris@3
   316
chris@3
   317
      // Fill prepared statement with values;
chris@3
   318
      // writes body to article table
chris@3
   319
      pstmtAddArticle1.setInt(1, newArticleID);
chris@3
   320
      pstmtAddArticle1.setBytes(2, article.getBody());
chris@3
   321
      pstmtAddArticle1.execute();
chris@3
   322
chris@3
   323
      // Add headers
chris@3
   324
      Enumeration headers = article.getAllHeaders();
chris@3
   325
      for(int n = 0; headers.hasMoreElements(); n++)
chris@3
   326
      {
chris@3
   327
        Header header = (Header)headers.nextElement();
chris@3
   328
        pstmtAddArticle2.setInt(1, newArticleID);
chris@3
   329
        pstmtAddArticle2.setString(2, header.getName().toLowerCase());
chris@3
   330
        pstmtAddArticle2.setString(3, 
chris@3
   331
          header.getValue().replaceAll("[\r\n]", ""));
chris@3
   332
        pstmtAddArticle2.setInt(4, n);
chris@3
   333
        pstmtAddArticle2.execute();
chris@3
   334
      }
chris@3
   335
      
chris@3
   336
      // For each newsgroup add a reference
chris@3
   337
      List<Group> groups = article.getGroups();
chris@3
   338
      for(Group group : groups)
chris@3
   339
      {
chris@3
   340
        pstmtAddArticle3.setLong(1, group.getInternalID());
chris@3
   341
        pstmtAddArticle3.setInt(2, newArticleID);
chris@3
   342
        pstmtAddArticle3.setLong(3, getMaxArticleIndex(group.getInternalID()) + 1);
chris@3
   343
        pstmtAddArticle3.execute();
chris@3
   344
      }
chris@3
   345
      
chris@3
   346
      // Write message-id to article_ids table
chris@3
   347
      this.pstmtAddArticle4.setInt(1, newArticleID);
chris@3
   348
      this.pstmtAddArticle4.setString(2, article.getMessageID());
chris@3
   349
      this.pstmtAddArticle4.execute();
chris@3
   350
chris@3
   351
      this.conn.commit();
chris@3
   352
      this.conn.setAutoCommit(true);
chris@3
   353
chris@3
   354
      this.restarts = 0; // Reset error count
chris@3
   355
    }
chris@3
   356
    catch(SQLException ex)
chris@3
   357
    {
chris@3
   358
      try
chris@3
   359
      {
chris@3
   360
        this.conn.rollback();  // Rollback changes
chris@3
   361
      }
chris@3
   362
      catch(SQLException ex2)
chris@3
   363
      {
chris@3
   364
        Log.msg("Rollback of addArticle() failed: " + ex2, false);
chris@3
   365
      }
chris@3
   366
      
chris@3
   367
      try
chris@3
   368
      {
chris@3
   369
        this.conn.setAutoCommit(true); // and release locks
chris@3
   370
      }
chris@3
   371
      catch(SQLException ex2)
chris@3
   372
      {
chris@3
   373
        Log.msg("setAutoCommit(true) of addArticle() failed: " + ex2, false);
chris@3
   374
      }
chris@3
   375
chris@3
   376
      restartConnection(ex);
chris@3
   377
      addArticle(article);
chris@3
   378
    }
chris@3
   379
  }
chris@3
   380
  
chris@3
   381
  /**
chris@3
   382
   * Adds a group to the JDBCDatabase. This method is not accessible via NNTP.
chris@3
   383
   * @param name
chris@3
   384
   * @throws java.sql.SQLException
chris@3
   385
   */
chris@3
   386
  @Override
chris@3
   387
  public void addGroup(String name, int flags)
chris@3
   388
    throws StorageBackendException
chris@3
   389
  {
chris@3
   390
    try
chris@3
   391
    {
chris@3
   392
      this.conn.setAutoCommit(false);
chris@3
   393
      pstmtAddGroup0.setString(1, name);
chris@3
   394
      pstmtAddGroup0.setInt(2, flags);
chris@3
   395
chris@3
   396
      pstmtAddGroup0.executeUpdate();
chris@3
   397
      this.conn.commit();
chris@3
   398
      this.conn.setAutoCommit(true);
chris@3
   399
      this.restarts = 0; // Reset error count
chris@3
   400
    }
chris@3
   401
    catch(SQLException ex)
chris@3
   402
    {
chris@3
   403
      try
chris@3
   404
      {
chris@3
   405
        this.conn.rollback();
chris@3
   406
        this.conn.setAutoCommit(true);
chris@3
   407
      }
chris@3
   408
      catch(SQLException ex2)
chris@3
   409
      {
chris@3
   410
        ex2.printStackTrace();
chris@3
   411
      }
chris@3
   412
chris@3
   413
      restartConnection(ex);
chris@3
   414
      addGroup(name, flags);
chris@3
   415
    }
chris@3
   416
  }
chris@3
   417
chris@3
   418
  @Override
chris@3
   419
  public void addEvent(long time, int type, long gid)
chris@3
   420
    throws StorageBackendException
chris@3
   421
  {
chris@3
   422
    try
chris@3
   423
    {
chris@3
   424
      this.conn.setAutoCommit(false);
chris@3
   425
      this.pstmtAddEvent.setLong(1, time);
chris@3
   426
      this.pstmtAddEvent.setInt(2, type);
chris@3
   427
      this.pstmtAddEvent.setLong(3, gid);
chris@3
   428
      this.pstmtAddEvent.executeUpdate();
chris@3
   429
      this.conn.commit();
chris@3
   430
      this.conn.setAutoCommit(true);
chris@3
   431
      this.restarts = 0;
chris@3
   432
    }
chris@3
   433
    catch(SQLException ex)
chris@3
   434
    {
chris@3
   435
      try
chris@3
   436
      {
chris@3
   437
        this.conn.rollback();
chris@3
   438
        this.conn.setAutoCommit(true);
chris@3
   439
      }
chris@3
   440
      catch(SQLException ex2)
chris@3
   441
      {
chris@3
   442
        ex2.printStackTrace();
chris@3
   443
      }
chris@3
   444
chris@3
   445
      restartConnection(ex);
chris@3
   446
      addEvent(time, type, gid);
chris@3
   447
    }
chris@3
   448
  }
chris@3
   449
chris@3
   450
  @Override
chris@3
   451
  public int countArticles()
chris@3
   452
    throws StorageBackendException
chris@3
   453
  {
chris@3
   454
    ResultSet rs = null;
chris@3
   455
chris@3
   456
    try
chris@3
   457
    {
chris@3
   458
      rs = this.pstmtCountArticles.executeQuery();
chris@3
   459
      if(rs.next())
chris@3
   460
      {
chris@3
   461
        return rs.getInt(1);
chris@3
   462
      }
chris@3
   463
      else
chris@3
   464
      {
chris@3
   465
        return -1;
chris@3
   466
      }
chris@3
   467
    }
chris@3
   468
    catch(SQLException ex)
chris@3
   469
    {
chris@3
   470
      restartConnection(ex);
chris@3
   471
      return countArticles();
chris@3
   472
    }
chris@3
   473
    finally
chris@3
   474
    {
chris@3
   475
      if(rs != null)
chris@3
   476
      {
chris@3
   477
        try
chris@3
   478
        {
chris@3
   479
          rs.close();
chris@3
   480
        }
chris@3
   481
        catch(SQLException ex)
chris@3
   482
        {
chris@3
   483
          ex.printStackTrace();
chris@3
   484
        }
chris@3
   485
        restarts = 0;
chris@3
   486
      }
chris@3
   487
    }
chris@3
   488
  }
chris@3
   489
chris@3
   490
  @Override
chris@3
   491
  public int countGroups()
chris@3
   492
    throws StorageBackendException
chris@3
   493
  {
chris@3
   494
    ResultSet rs = null;
chris@3
   495
chris@3
   496
    try
chris@3
   497
    {
chris@3
   498
      rs = this.pstmtCountGroups.executeQuery();
chris@3
   499
      if(rs.next())
chris@3
   500
      {
chris@3
   501
        return rs.getInt(1);
chris@3
   502
      }
chris@3
   503
      else
chris@3
   504
      {
chris@3
   505
        return -1;
chris@3
   506
      }
chris@3
   507
    }
chris@3
   508
    catch(SQLException ex)
chris@3
   509
    {
chris@3
   510
      restartConnection(ex);
chris@3
   511
      return countGroups();
chris@3
   512
    }
chris@3
   513
    finally
chris@3
   514
    {
chris@3
   515
      if(rs != null)
chris@3
   516
      {
chris@3
   517
        try
chris@3
   518
        {
chris@3
   519
          rs.close();
chris@3
   520
        }
chris@3
   521
        catch(SQLException ex)
chris@3
   522
        {
chris@3
   523
          ex.printStackTrace();
chris@3
   524
        }
chris@3
   525
        restarts = 0;
chris@3
   526
      }
chris@3
   527
    }
chris@3
   528
  }
chris@3
   529
chris@3
   530
  @Override
chris@3
   531
  public void delete(final String messageID)
chris@3
   532
    throws StorageBackendException
chris@3
   533
  {
chris@3
   534
    try
chris@3
   535
    {
chris@3
   536
      this.conn.setAutoCommit(false);
chris@3
   537
      
chris@3
   538
      this.pstmtDeleteArticle0.setString(1, messageID);
chris@3
   539
      int rs = this.pstmtDeleteArticle0.executeUpdate();
chris@3
   540
      
chris@3
   541
      // We do not trust the ON DELETE CASCADE functionality to delete
chris@3
   542
      // orphaned references...
chris@3
   543
      this.pstmtDeleteArticle1.setString(1, messageID);
chris@3
   544
      rs = this.pstmtDeleteArticle1.executeUpdate();
chris@3
   545
chris@3
   546
      this.pstmtDeleteArticle2.setString(1, messageID);
chris@3
   547
      rs = this.pstmtDeleteArticle2.executeUpdate();
chris@3
   548
chris@3
   549
      this.pstmtDeleteArticle3.setString(1, messageID);
chris@3
   550
      rs = this.pstmtDeleteArticle3.executeUpdate();
chris@3
   551
      
chris@3
   552
      this.conn.commit();
chris@3
   553
      this.conn.setAutoCommit(true);
chris@3
   554
    }
chris@3
   555
    catch(SQLException ex)
chris@3
   556
    {
chris@3
   557
      throw new StorageBackendException(ex);
chris@3
   558
    }
chris@3
   559
  }
chris@3
   560
chris@3
   561
  @Override
chris@3
   562
  public Article getArticle(String messageID)
chris@3
   563
    throws StorageBackendException
chris@3
   564
  {
chris@3
   565
    ResultSet rs = null;
chris@3
   566
    try
chris@3
   567
    {
chris@3
   568
      pstmtGetArticle0.setString(1, messageID);
chris@3
   569
      rs = pstmtGetArticle0.executeQuery();
chris@3
   570
chris@3
   571
      if(!rs.next())
chris@3
   572
      {
chris@3
   573
        return null;
chris@3
   574
      }
chris@3
   575
      else
chris@3
   576
      {
chris@3
   577
        byte[] body     = rs.getBytes("body");
chris@3
   578
        String headers  = getArticleHeaders(rs.getInt("article_id"));
chris@3
   579
        return new Article(headers, body);
chris@3
   580
      }
chris@3
   581
    }
chris@3
   582
    catch(SQLException ex)
chris@3
   583
    {
chris@3
   584
      restartConnection(ex);
chris@3
   585
      return getArticle(messageID);
chris@3
   586
    }
chris@3
   587
    finally
chris@3
   588
    {
chris@3
   589
      if(rs != null)
chris@3
   590
      {
chris@3
   591
        try
chris@3
   592
        {
chris@3
   593
          rs.close();
chris@3
   594
        }
chris@3
   595
        catch(SQLException ex)
chris@3
   596
        {
chris@3
   597
          ex.printStackTrace();
chris@3
   598
        }
chris@3
   599
        restarts = 0; // Reset error count
chris@3
   600
      }
chris@3
   601
    }
chris@3
   602
  }
chris@3
   603
  
chris@3
   604
  /**
chris@3
   605
   * Retrieves an article by its ID.
chris@3
   606
   * @param articleID
chris@3
   607
   * @return
chris@3
   608
   * @throws StorageBackendException
chris@3
   609
   */
chris@3
   610
  @Override
chris@3
   611
  public Article getArticle(long articleIndex, long gid)
chris@3
   612
    throws StorageBackendException
chris@3
   613
  {  
chris@3
   614
    ResultSet rs = null;
chris@3
   615
chris@3
   616
    try
chris@3
   617
    {
chris@3
   618
      this.pstmtGetArticle1.setLong(1, articleIndex);
chris@3
   619
      this.pstmtGetArticle1.setLong(2, gid);
chris@3
   620
chris@3
   621
      rs = this.pstmtGetArticle1.executeQuery();
chris@3
   622
chris@3
   623
      if(rs.next())
chris@3
   624
      {
chris@3
   625
        byte[] body    = rs.getBytes("body");
chris@3
   626
        String headers = getArticleHeaders(rs.getInt("article_id"));
chris@3
   627
        return new Article(headers, body);
chris@3
   628
      }
chris@3
   629
      else
chris@3
   630
      {
chris@3
   631
        return null;
chris@3
   632
      }
chris@3
   633
    }
chris@3
   634
    catch(SQLException ex)
chris@3
   635
    {
chris@3
   636
      restartConnection(ex);
chris@3
   637
      return getArticle(articleIndex, gid);
chris@3
   638
    }
chris@3
   639
    finally
chris@3
   640
    {
chris@3
   641
      if(rs != null)
chris@3
   642
      {
chris@3
   643
        try
chris@3
   644
        {
chris@3
   645
          rs.close();
chris@3
   646
        }
chris@3
   647
        catch(SQLException ex)
chris@3
   648
        {
chris@3
   649
          ex.printStackTrace();
chris@3
   650
        }
chris@3
   651
        restarts = 0;
chris@3
   652
      }
chris@3
   653
    }
chris@3
   654
  }
chris@3
   655
chris@3
   656
  /**
chris@3
   657
   * Searches for fitting header values using the given regular expression.
chris@3
   658
   * @param group
chris@3
   659
   * @param start
chris@3
   660
   * @param end
chris@3
   661
   * @param headerKey
chris@3
   662
   * @param pattern
chris@3
   663
   * @return
chris@3
   664
   * @throws StorageBackendException
chris@3
   665
   */
chris@3
   666
  @Override
chris@3
   667
  public List<Pair<Long, String>> getArticleHeaders(Channel group, long start,
chris@3
   668
    long end, String headerKey, String patStr)
chris@3
   669
    throws StorageBackendException, PatternSyntaxException
chris@3
   670
  {
chris@3
   671
    ResultSet rs = null;
chris@3
   672
    List<Pair<Long, String>> heads = new ArrayList<Pair<Long, String>>();
chris@3
   673
chris@3
   674
    try
chris@3
   675
    {
chris@3
   676
      this.pstmtGetArticleHeaders1.setString(1, group.getName());
chris@3
   677
      this.pstmtGetArticleHeaders1.setString(2, headerKey);
chris@3
   678
      this.pstmtGetArticleHeaders1.setLong(3, start);
chris@3
   679
chris@3
   680
      rs = this.pstmtGetArticleHeaders1.executeQuery();
chris@3
   681
chris@3
   682
      // Convert the "NNTP" regex to Java regex
chris@3
   683
      patStr = patStr.replace("*", ".*");
chris@3
   684
      Pattern pattern = Pattern.compile(patStr);
chris@3
   685
chris@3
   686
      while(rs.next())
chris@3
   687
      {
chris@3
   688
        Long articleIndex = rs.getLong(1);
chris@3
   689
        if(end < 0 || articleIndex <= end) // Match start is done via SQL
chris@3
   690
        {
chris@3
   691
          String headerValue  = rs.getString(2);
chris@3
   692
          Matcher matcher = pattern.matcher(headerValue);
chris@3
   693
          if(matcher.matches())
chris@3
   694
          {
chris@3
   695
            heads.add(new Pair<Long, String>(articleIndex, headerValue));
chris@3
   696
          }
chris@3
   697
        }
chris@3
   698
      }
chris@3
   699
    }
chris@3
   700
    catch(SQLException ex)
chris@3
   701
    {
chris@3
   702
      restartConnection(ex);
chris@3
   703
      return getArticleHeaders(group, start, end, headerKey, patStr);
chris@3
   704
    }
chris@3
   705
    finally
chris@3
   706
    {
chris@3
   707
      if(rs != null)
chris@3
   708
      {
chris@3
   709
        try
chris@3
   710
        {
chris@3
   711
          rs.close();
chris@3
   712
        }
chris@3
   713
        catch(SQLException ex)
chris@3
   714
        {
chris@3
   715
          ex.printStackTrace();
chris@3
   716
        }
chris@3
   717
      }
chris@3
   718
    }
chris@3
   719
chris@3
   720
    return heads;
chris@3
   721
  }
chris@3
   722
chris@3
   723
  private String getArticleHeaders(long articleID)
chris@3
   724
    throws StorageBackendException
chris@3
   725
  {
chris@3
   726
    ResultSet rs = null;
chris@3
   727
    
chris@3
   728
    try
chris@3
   729
    {
chris@3
   730
      this.pstmtGetArticleHeaders0.setLong(1, articleID);
chris@3
   731
      rs = this.pstmtGetArticleHeaders0.executeQuery();
chris@3
   732
      
chris@3
   733
      StringBuilder buf = new StringBuilder();
chris@3
   734
      if(rs.next())
chris@3
   735
      {
chris@3
   736
        for(;;)
chris@3
   737
        {
chris@3
   738
          buf.append(rs.getString(1)); // key
chris@3
   739
          buf.append(": ");
chris@3
   740
          String foldedValue = MimeUtility.fold(0, rs.getString(2));
chris@3
   741
          buf.append(foldedValue); // value
chris@3
   742
          if(rs.next())
chris@3
   743
          {
chris@3
   744
            buf.append("\r\n");
chris@3
   745
          }
chris@3
   746
          else
chris@3
   747
          {
chris@3
   748
            break;
chris@3
   749
          }
chris@3
   750
        }
chris@3
   751
      }
chris@3
   752
      
chris@3
   753
      return buf.toString();
chris@3
   754
    }
chris@3
   755
    catch(SQLException ex)
chris@3
   756
    {
chris@3
   757
      restartConnection(ex);
chris@3
   758
      return getArticleHeaders(articleID);
chris@3
   759
    }
chris@3
   760
    finally
chris@3
   761
    {
chris@3
   762
      if(rs != null)
chris@3
   763
      {
chris@3
   764
        try
chris@3
   765
        {
chris@3
   766
          rs.close();
chris@3
   767
        }
chris@3
   768
        catch(SQLException ex)
chris@3
   769
        {
chris@3
   770
          ex.printStackTrace();
chris@3
   771
        }
chris@3
   772
      }
chris@3
   773
    }
chris@3
   774
  }
chris@3
   775
chris@3
   776
  @Override
chris@3
   777
  public long getArticleIndex(Article article, Group group)
chris@3
   778
    throws StorageBackendException
chris@3
   779
  {
chris@3
   780
    ResultSet rs = null;
chris@3
   781
chris@3
   782
    try
chris@3
   783
    {
chris@3
   784
      this.pstmtGetArticleIndex.setString(1, article.getMessageID());
chris@3
   785
      this.pstmtGetArticleIndex.setLong(2, group.getInternalID());
chris@3
   786
      
chris@3
   787
      rs = this.pstmtGetArticleIndex.executeQuery();
chris@3
   788
      if(rs.next())
chris@3
   789
      {
chris@3
   790
        return rs.getLong(1);
chris@3
   791
      }
chris@3
   792
      else
chris@3
   793
      {
chris@3
   794
        return -1;
chris@3
   795
      }
chris@3
   796
    }
chris@3
   797
    catch(SQLException ex)
chris@3
   798
    {
chris@3
   799
      restartConnection(ex);
chris@3
   800
      return getArticleIndex(article, group);
chris@3
   801
    }
chris@3
   802
    finally
chris@3
   803
    {
chris@3
   804
      if(rs != null)
chris@3
   805
      {
chris@3
   806
        try
chris@3
   807
        {
chris@3
   808
          rs.close();
chris@3
   809
        }
chris@3
   810
        catch(SQLException ex)
chris@3
   811
        {
chris@3
   812
          ex.printStackTrace();
chris@3
   813
        }
chris@3
   814
      }
chris@3
   815
    }
chris@3
   816
  }
chris@3
   817
  
chris@3
   818
  /**
chris@3
   819
   * Returns a list of Long/Article Pairs.
chris@3
   820
   * @throws java.sql.SQLException
chris@3
   821
   */
chris@3
   822
  @Override
chris@3
   823
  public List<Pair<Long, ArticleHead>> getArticleHeads(Group group, long first,
chris@3
   824
    long last)
chris@3
   825
    throws StorageBackendException
chris@3
   826
  {
chris@3
   827
    ResultSet rs = null;
chris@3
   828
chris@3
   829
    try
chris@3
   830
    {
chris@3
   831
      this.pstmtGetArticleHeads.setLong(1, group.getInternalID());
chris@3
   832
      this.pstmtGetArticleHeads.setLong(2, first);
chris@3
   833
      this.pstmtGetArticleHeads.setLong(3, last);
chris@3
   834
      rs = pstmtGetArticleHeads.executeQuery();
chris@3
   835
chris@3
   836
      List<Pair<Long, ArticleHead>> articles 
chris@3
   837
        = new ArrayList<Pair<Long, ArticleHead>>();
chris@3
   838
chris@3
   839
      while (rs.next())
chris@3
   840
      {
chris@3
   841
        long aid  = rs.getLong("article_id");
chris@3
   842
        long aidx = rs.getLong("article_index");
chris@3
   843
        String headers = getArticleHeaders(aid);
chris@3
   844
        articles.add(new Pair<Long, ArticleHead>(aidx, 
chris@3
   845
                        new ArticleHead(headers)));
chris@3
   846
      }
chris@3
   847
chris@3
   848
      return articles;
chris@3
   849
    }
chris@3
   850
    catch(SQLException ex)
chris@3
   851
    {
chris@3
   852
      restartConnection(ex);
chris@3
   853
      return getArticleHeads(group, first, last);
chris@3
   854
    }
chris@3
   855
    finally
chris@3
   856
    {
chris@3
   857
      if(rs != null)
chris@3
   858
      {
chris@3
   859
        try
chris@3
   860
        {
chris@3
   861
          rs.close();
chris@3
   862
        }
chris@3
   863
        catch(SQLException ex)
chris@3
   864
        {
chris@3
   865
          ex.printStackTrace();
chris@3
   866
        }
chris@3
   867
      }
chris@3
   868
    }
chris@3
   869
  }
chris@3
   870
chris@3
   871
  @Override
chris@3
   872
  public List<Long> getArticleNumbers(long gid)
chris@3
   873
    throws StorageBackendException
chris@3
   874
  {
chris@3
   875
    ResultSet rs = null;
chris@3
   876
    try
chris@3
   877
    {
chris@3
   878
      List<Long> ids = new ArrayList<Long>();
chris@3
   879
      this.pstmtGetArticleIDs.setLong(1, gid);
chris@3
   880
      rs = this.pstmtGetArticleIDs.executeQuery();
chris@3
   881
      while(rs.next())
chris@3
   882
      {
chris@3
   883
        ids.add(rs.getLong(1));
chris@3
   884
      }
chris@3
   885
      return ids;
chris@3
   886
    }
chris@3
   887
    catch(SQLException ex)
chris@3
   888
    {
chris@3
   889
      restartConnection(ex);
chris@3
   890
      return getArticleNumbers(gid);
chris@3
   891
    }
chris@3
   892
    finally
chris@3
   893
    {
chris@3
   894
      if(rs != null)
chris@3
   895
      {
chris@3
   896
        try
chris@3
   897
        {
chris@3
   898
          rs.close();
chris@3
   899
          restarts = 0; // Clear the restart count after successful request
chris@3
   900
        }
chris@3
   901
        catch(SQLException ex)
chris@3
   902
        {
chris@3
   903
          ex.printStackTrace();
chris@3
   904
        }
chris@3
   905
      }
chris@3
   906
    }
chris@3
   907
  }
chris@3
   908
chris@3
   909
  @Override
chris@3
   910
  public String getConfigValue(String key)
chris@3
   911
    throws StorageBackendException
chris@3
   912
  {
chris@3
   913
    ResultSet rs = null;
chris@3
   914
    try
chris@3
   915
    {
chris@3
   916
      this.pstmtGetConfigValue.setString(1, key);
chris@3
   917
chris@3
   918
      rs = this.pstmtGetConfigValue.executeQuery();
chris@3
   919
      if(rs.next())
chris@3
   920
      {
chris@3
   921
        return rs.getString(1); // First data on index 1 not 0
chris@3
   922
      }
chris@3
   923
      else
chris@3
   924
      {
chris@3
   925
        return null;
chris@3
   926
      }
chris@3
   927
    }
chris@3
   928
    catch(SQLException ex)
chris@3
   929
    {
chris@3
   930
      restartConnection(ex);
chris@3
   931
      return getConfigValue(key);
chris@3
   932
    }
chris@3
   933
    finally
chris@3
   934
    {
chris@3
   935
      if(rs != null)
chris@3
   936
      {
chris@3
   937
        try
chris@3
   938
        {
chris@3
   939
          rs.close();
chris@3
   940
        }
chris@3
   941
        catch(SQLException ex)
chris@3
   942
        {
chris@3
   943
          ex.printStackTrace();
chris@3
   944
        }
chris@3
   945
        restarts = 0; // Clear the restart count after successful request
chris@3
   946
      }
chris@3
   947
    }
chris@3
   948
  }
chris@3
   949
chris@3
   950
  @Override
chris@3
   951
  public int getEventsCount(int type, long start, long end, Channel channel)
chris@3
   952
    throws StorageBackendException
chris@3
   953
  {
chris@3
   954
    ResultSet rs = null;
chris@3
   955
    
chris@3
   956
    try
chris@3
   957
    {
chris@3
   958
      if(channel == null)
chris@3
   959
      {
chris@3
   960
        this.pstmtGetEventsCount0.setInt(1, type);
chris@3
   961
        this.pstmtGetEventsCount0.setLong(2, start);
chris@3
   962
        this.pstmtGetEventsCount0.setLong(3, end);
chris@3
   963
        rs = this.pstmtGetEventsCount0.executeQuery();
chris@3
   964
      }
chris@3
   965
      else
chris@3
   966
      {
chris@3
   967
        this.pstmtGetEventsCount1.setInt(1, type);
chris@3
   968
        this.pstmtGetEventsCount1.setLong(2, start);
chris@3
   969
        this.pstmtGetEventsCount1.setLong(3, end);
chris@3
   970
        this.pstmtGetEventsCount1.setLong(4, channel.getInternalID());
chris@3
   971
        rs = this.pstmtGetEventsCount1.executeQuery();
chris@3
   972
      }
chris@3
   973
      
chris@3
   974
      if(rs.next())
chris@3
   975
      {
chris@3
   976
        return rs.getInt(1);
chris@3
   977
      }
chris@3
   978
      else
chris@3
   979
      {
chris@3
   980
        return -1;
chris@3
   981
      }
chris@3
   982
    }
chris@3
   983
    catch(SQLException ex)
chris@3
   984
    {
chris@3
   985
      restartConnection(ex);
chris@3
   986
      return getEventsCount(type, start, end, channel);
chris@3
   987
    }
chris@3
   988
    finally
chris@3
   989
    {
chris@3
   990
      if(rs != null)
chris@3
   991
      {
chris@3
   992
        try
chris@3
   993
        {
chris@3
   994
          rs.close();
chris@3
   995
        }
chris@3
   996
        catch(SQLException ex)
chris@3
   997
        {
chris@3
   998
          ex.printStackTrace();
chris@3
   999
        }
chris@3
  1000
      }
chris@3
  1001
    }
chris@3
  1002
  }
chris@3
  1003
  
chris@3
  1004
  /**
chris@3
  1005
   * Reads all Groups from the JDBCDatabase.
chris@3
  1006
   * @return
chris@3
  1007
   * @throws StorageBackendException
chris@3
  1008
   */
chris@3
  1009
  @Override
chris@3
  1010
  public List<Channel> getGroups()
chris@3
  1011
    throws StorageBackendException
chris@3
  1012
  {
chris@3
  1013
    ResultSet   rs;
chris@3
  1014
    List<Channel> buffer = new ArrayList<Channel>();
chris@3
  1015
    Statement   stmt   = null;
chris@3
  1016
chris@3
  1017
    try
chris@3
  1018
    {
chris@3
  1019
      stmt = conn.createStatement();
chris@3
  1020
      rs = stmt.executeQuery("SELECT * FROM groups ORDER BY name");
chris@3
  1021
chris@3
  1022
      while(rs.next())
chris@3
  1023
      {
chris@3
  1024
        String name  = rs.getString("name");
chris@3
  1025
        long   id    = rs.getLong("group_id");
chris@3
  1026
        int    flags = rs.getInt("flags");
chris@3
  1027
        
chris@3
  1028
        Group group = new Group(name, id, flags);
chris@3
  1029
        buffer.add(group);
chris@3
  1030
      }
chris@3
  1031
chris@3
  1032
      return buffer;
chris@3
  1033
    }
chris@3
  1034
    catch(SQLException ex)
chris@3
  1035
    {
chris@3
  1036
      restartConnection(ex);
chris@3
  1037
      return getGroups();
chris@3
  1038
    }
chris@3
  1039
    finally
chris@3
  1040
    {
chris@3
  1041
      if(stmt != null)
chris@3
  1042
      {
chris@3
  1043
        try
chris@3
  1044
        {
chris@3
  1045
          stmt.close(); // Implicitely closes ResultSets
chris@3
  1046
        }
chris@3
  1047
        catch(SQLException ex)
chris@3
  1048
        {
chris@3
  1049
          ex.printStackTrace();
chris@3
  1050
        }
chris@3
  1051
      }
chris@3
  1052
    }
chris@3
  1053
  }
chris@3
  1054
chris@3
  1055
  @Override
chris@3
  1056
  public List<String> getGroupsForList(InternetAddress listAddress)
chris@3
  1057
    throws StorageBackendException
chris@3
  1058
  {
chris@3
  1059
    ResultSet rs = null;
chris@3
  1060
    
chris@3
  1061
    try
chris@3
  1062
    {
chris@3
  1063
      this.pstmtGetGroupForList.setString(1, listAddress.getAddress());
chris@3
  1064
chris@3
  1065
      rs = this.pstmtGetGroupForList.executeQuery();
chris@3
  1066
      List<String> groups = new ArrayList<String>();
chris@3
  1067
      while(rs.next())
chris@3
  1068
      {
chris@3
  1069
        String group = rs.getString(1);
chris@3
  1070
        groups.add(group);
chris@3
  1071
      }
chris@3
  1072
      return groups;
chris@3
  1073
    }
chris@3
  1074
    catch(SQLException ex)
chris@3
  1075
    {
chris@3
  1076
      restartConnection(ex);
chris@3
  1077
      return getGroupsForList(listAddress);
chris@3
  1078
    }
chris@3
  1079
    finally
chris@3
  1080
    {
chris@3
  1081
      if(rs != null)
chris@3
  1082
      {
chris@3
  1083
        try
chris@3
  1084
        {
chris@3
  1085
          rs.close();
chris@3
  1086
        }
chris@3
  1087
        catch(SQLException ex)
chris@3
  1088
        {
chris@3
  1089
          ex.printStackTrace();
chris@3
  1090
        }
chris@3
  1091
      }
chris@3
  1092
    }
chris@3
  1093
  }
chris@3
  1094
  
chris@3
  1095
  /**
chris@3
  1096
   * Returns the Group that is identified by the name.
chris@3
  1097
   * @param name
chris@3
  1098
   * @return
chris@3
  1099
   * @throws StorageBackendException
chris@3
  1100
   */
chris@3
  1101
  @Override
chris@3
  1102
  public Group getGroup(String name)
chris@3
  1103
    throws StorageBackendException
chris@3
  1104
  {
chris@3
  1105
    ResultSet rs = null;
chris@3
  1106
    
chris@3
  1107
    try
chris@3
  1108
    {
chris@3
  1109
      this.pstmtGetGroup0.setString(1, name);
chris@3
  1110
      rs = this.pstmtGetGroup0.executeQuery();
chris@3
  1111
chris@3
  1112
      if (!rs.next())
chris@3
  1113
      {
chris@3
  1114
        return null;
chris@3
  1115
      }
chris@3
  1116
      else
chris@3
  1117
      {
chris@3
  1118
        long id = rs.getLong("group_id");
chris@3
  1119
        int flags = rs.getInt("flags");
chris@3
  1120
        return new Group(name, id, flags);
chris@3
  1121
      }
chris@3
  1122
    }
chris@3
  1123
    catch(SQLException ex)
chris@3
  1124
    {
chris@3
  1125
      restartConnection(ex);
chris@3
  1126
      return getGroup(name);
chris@3
  1127
    }
chris@3
  1128
    finally
chris@3
  1129
    {
chris@3
  1130
      if(rs != null)
chris@3
  1131
      {
chris@3
  1132
        try
chris@3
  1133
        {
chris@3
  1134
          rs.close();
chris@3
  1135
        }
chris@3
  1136
        catch(SQLException ex)
chris@3
  1137
        {
chris@3
  1138
          ex.printStackTrace();
chris@3
  1139
        }
chris@3
  1140
      }
chris@3
  1141
    }
chris@3
  1142
  }
chris@3
  1143
chris@3
  1144
  @Override
cli@12
  1145
  public List<String> getListsForGroup(String group)
chris@3
  1146
    throws StorageBackendException
chris@3
  1147
  {
cli@12
  1148
    ResultSet     rs    = null;
cli@12
  1149
    List<String>  lists = new ArrayList<String>();
chris@3
  1150
chris@3
  1151
    try
chris@3
  1152
    {
chris@3
  1153
      this.pstmtGetListForGroup.setString(1, group);
chris@3
  1154
      rs = this.pstmtGetListForGroup.executeQuery();
cli@12
  1155
cli@12
  1156
      while(rs.next())
chris@3
  1157
      {
cli@12
  1158
        lists.add(rs.getString(1));
chris@3
  1159
      }
cli@12
  1160
      return lists;
chris@3
  1161
    }
chris@3
  1162
    catch(SQLException ex)
chris@3
  1163
    {
chris@3
  1164
      restartConnection(ex);
cli@12
  1165
      return getListsForGroup(group);
chris@3
  1166
    }
chris@3
  1167
    finally
chris@3
  1168
    {
chris@3
  1169
      if(rs != null)
chris@3
  1170
      {
chris@3
  1171
        try
chris@3
  1172
        {
chris@3
  1173
          rs.close();
chris@3
  1174
        }
chris@3
  1175
        catch(SQLException ex)
chris@3
  1176
        {
chris@3
  1177
          ex.printStackTrace();
chris@3
  1178
        }
chris@3
  1179
      }
chris@3
  1180
    }
chris@3
  1181
  }
chris@3
  1182
  
chris@3
  1183
  private int getMaxArticleIndex(long groupID)
chris@3
  1184
    throws StorageBackendException
chris@3
  1185
  {
chris@3
  1186
    ResultSet rs    = null;
chris@3
  1187
chris@3
  1188
    try
chris@3
  1189
    {
chris@3
  1190
      this.pstmtGetMaxArticleIndex.setLong(1, groupID);
chris@3
  1191
      rs = this.pstmtGetMaxArticleIndex.executeQuery();
chris@3
  1192
chris@3
  1193
      int maxIndex = 0;
chris@3
  1194
      if (rs.next())
chris@3
  1195
      {
chris@3
  1196
        maxIndex = rs.getInt(1);
chris@3
  1197
      }
chris@3
  1198
chris@3
  1199
      return maxIndex;
chris@3
  1200
    }
chris@3
  1201
    catch(SQLException ex)
chris@3
  1202
    {
chris@3
  1203
      restartConnection(ex);
chris@3
  1204
      return getMaxArticleIndex(groupID);
chris@3
  1205
    }
chris@3
  1206
    finally
chris@3
  1207
    {
chris@3
  1208
      if(rs != null)
chris@3
  1209
      {
chris@3
  1210
        try
chris@3
  1211
        {
chris@3
  1212
          rs.close();
chris@3
  1213
        }
chris@3
  1214
        catch(SQLException ex)
chris@3
  1215
        {
chris@3
  1216
          ex.printStackTrace();
chris@3
  1217
        }
chris@3
  1218
      }
chris@3
  1219
    }
chris@3
  1220
  }
chris@3
  1221
  
chris@3
  1222
  private int getMaxArticleID()
chris@3
  1223
    throws StorageBackendException
chris@3
  1224
  {
chris@3
  1225
    ResultSet rs    = null;
chris@3
  1226
chris@3
  1227
    try
chris@3
  1228
    {
chris@3
  1229
      rs = this.pstmtGetMaxArticleID.executeQuery();
chris@3
  1230
chris@3
  1231
      int maxIndex = 0;
chris@3
  1232
      if (rs.next())
chris@3
  1233
      {
chris@3
  1234
        maxIndex = rs.getInt(1);
chris@3
  1235
      }
chris@3
  1236
chris@3
  1237
      return maxIndex;
chris@3
  1238
    }
chris@3
  1239
    catch(SQLException ex)
chris@3
  1240
    {
chris@3
  1241
      restartConnection(ex);
chris@3
  1242
      return getMaxArticleID();
chris@3
  1243
    }
chris@3
  1244
    finally
chris@3
  1245
    {
chris@3
  1246
      if(rs != null)
chris@3
  1247
      {
chris@3
  1248
        try
chris@3
  1249
        {
chris@3
  1250
          rs.close();
chris@3
  1251
        }
chris@3
  1252
        catch(SQLException ex)
chris@3
  1253
        {
chris@3
  1254
          ex.printStackTrace();
chris@3
  1255
        }
chris@3
  1256
      }
chris@3
  1257
    }
chris@3
  1258
  }
chris@3
  1259
chris@3
  1260
  @Override
chris@3
  1261
  public int getLastArticleNumber(Group group)
chris@3
  1262
    throws StorageBackendException
chris@3
  1263
  {
chris@3
  1264
    ResultSet rs = null;
chris@3
  1265
chris@3
  1266
    try
chris@3
  1267
    {
chris@3
  1268
      this.pstmtGetLastArticleNumber.setLong(1, group.getInternalID());
chris@3
  1269
      rs = this.pstmtGetLastArticleNumber.executeQuery();
chris@3
  1270
      if (rs.next())
chris@3
  1271
      {
chris@3
  1272
        return rs.getInt(1);
chris@3
  1273
      }
chris@3
  1274
      else
chris@3
  1275
      {
chris@3
  1276
        return 0;
chris@3
  1277
      }
chris@3
  1278
    }
chris@3
  1279
    catch(SQLException ex)
chris@3
  1280
    {
chris@3
  1281
      restartConnection(ex);
chris@3
  1282
      return getLastArticleNumber(group);
chris@3
  1283
    }
chris@3
  1284
    finally
chris@3
  1285
    {
chris@3
  1286
      if(rs != null)
chris@3
  1287
      {
chris@3
  1288
        try
chris@3
  1289
        {
chris@3
  1290
          rs.close();
chris@3
  1291
        }
chris@3
  1292
        catch(SQLException ex)
chris@3
  1293
        {
chris@3
  1294
          ex.printStackTrace();
chris@3
  1295
        }
chris@3
  1296
      }
chris@3
  1297
    }
chris@3
  1298
  }
chris@3
  1299
chris@3
  1300
  @Override
chris@3
  1301
  public int getFirstArticleNumber(Group group)
chris@3
  1302
    throws StorageBackendException
chris@3
  1303
  {
chris@3
  1304
    ResultSet rs = null;
chris@3
  1305
    try
chris@3
  1306
    {
chris@3
  1307
      this.pstmtGetFirstArticleNumber.setLong(1, group.getInternalID());
chris@3
  1308
      rs = this.pstmtGetFirstArticleNumber.executeQuery();
chris@3
  1309
      if(rs.next())
chris@3
  1310
      {
chris@3
  1311
        return rs.getInt(1);
chris@3
  1312
      }
chris@3
  1313
      else
chris@3
  1314
      {
chris@3
  1315
        return 0;
chris@3
  1316
      }
chris@3
  1317
    }
chris@3
  1318
    catch(SQLException ex)
chris@3
  1319
    {
chris@3
  1320
      restartConnection(ex);
chris@3
  1321
      return getFirstArticleNumber(group);
chris@3
  1322
    }
chris@3
  1323
    finally
chris@3
  1324
    {
chris@3
  1325
      if(rs != null)
chris@3
  1326
      {
chris@3
  1327
        try
chris@3
  1328
        {
chris@3
  1329
          rs.close();
chris@3
  1330
        }
chris@3
  1331
        catch(SQLException ex)
chris@3
  1332
        {
chris@3
  1333
          ex.printStackTrace();
chris@3
  1334
        }
chris@3
  1335
      }
chris@3
  1336
    }
chris@3
  1337
  }
chris@3
  1338
  
chris@3
  1339
  /**
chris@3
  1340
   * Returns a group name identified by the given id.
chris@3
  1341
   * @param id
chris@3
  1342
   * @return
chris@3
  1343
   * @throws StorageBackendException
chris@3
  1344
   */
chris@3
  1345
  public String getGroup(int id)
chris@3
  1346
    throws StorageBackendException
chris@3
  1347
  {
chris@3
  1348
    ResultSet rs = null;
chris@3
  1349
chris@3
  1350
    try
chris@3
  1351
    {
chris@3
  1352
      this.pstmtGetGroup1.setInt(1, id);
chris@3
  1353
      rs = this.pstmtGetGroup1.executeQuery();
chris@3
  1354
chris@3
  1355
      if (rs.next())
chris@3
  1356
      {
chris@3
  1357
        return rs.getString(1);
chris@3
  1358
      }
chris@3
  1359
      else
chris@3
  1360
      {
chris@3
  1361
        return null;
chris@3
  1362
      }
chris@3
  1363
    }
chris@3
  1364
    catch(SQLException ex)
chris@3
  1365
    {
chris@3
  1366
      restartConnection(ex);
chris@3
  1367
      return getGroup(id);
chris@3
  1368
    }
chris@3
  1369
    finally
chris@3
  1370
    {
chris@3
  1371
      if(rs != null)
chris@3
  1372
      {
chris@3
  1373
        try
chris@3
  1374
        {
chris@3
  1375
          rs.close();
chris@3
  1376
        }
chris@3
  1377
        catch(SQLException ex)
chris@3
  1378
        {
chris@3
  1379
          ex.printStackTrace();
chris@3
  1380
        }
chris@3
  1381
      }
chris@3
  1382
    }
chris@3
  1383
  }
chris@3
  1384
chris@3
  1385
  @Override
chris@3
  1386
  public double getEventsPerHour(int key, long gid)
chris@3
  1387
    throws StorageBackendException
chris@3
  1388
  {
chris@3
  1389
    String gidquery = "";
chris@3
  1390
    if(gid >= 0)
chris@3
  1391
    {
chris@3
  1392
      gidquery = " AND group_id = " + gid;
chris@3
  1393
    }
chris@3
  1394
    
chris@3
  1395
    Statement stmt = null;
chris@3
  1396
    ResultSet rs   = null;
chris@3
  1397
    
chris@3
  1398
    try
chris@3
  1399
    {
chris@3
  1400
      stmt = this.conn.createStatement();
chris@3
  1401
      rs = stmt.executeQuery("SELECT Count(*) / (Max(event_time) - Min(event_time))" +
chris@3
  1402
        " * 1000 * 60 * 60 FROM events WHERE event_key = " + key + gidquery);
chris@3
  1403
      
chris@3
  1404
      if(rs.next())
chris@3
  1405
      {
chris@3
  1406
        restarts = 0; // reset error count
chris@3
  1407
        return rs.getDouble(1);
chris@3
  1408
      }
chris@3
  1409
      else
chris@3
  1410
      {
chris@3
  1411
        return Double.NaN;
chris@3
  1412
      }
chris@3
  1413
    }
chris@3
  1414
    catch(SQLException ex)
chris@3
  1415
    {
chris@3
  1416
      restartConnection(ex);
chris@3
  1417
      return getEventsPerHour(key, gid);
chris@3
  1418
    }
chris@3
  1419
    finally
chris@3
  1420
    {
chris@3
  1421
      try
chris@3
  1422
      {
chris@3
  1423
        if(stmt != null)
chris@3
  1424
        {
chris@3
  1425
          stmt.close(); // Implicitely closes the result sets
chris@3
  1426
        }
chris@3
  1427
      }
chris@3
  1428
      catch(SQLException ex)
chris@3
  1429
      {
chris@3
  1430
        ex.printStackTrace();
chris@3
  1431
      }
chris@3
  1432
    }
chris@3
  1433
  }
chris@3
  1434
chris@3
  1435
  @Override
chris@3
  1436
  public String getOldestArticle()
chris@3
  1437
    throws StorageBackendException
chris@3
  1438
  {
chris@3
  1439
    ResultSet rs = null;
chris@3
  1440
chris@3
  1441
    try
chris@3
  1442
    {
chris@3
  1443
      rs = this.pstmtGetOldestArticle.executeQuery();
chris@3
  1444
      if(rs.next())
chris@3
  1445
      {
chris@3
  1446
        return rs.getString(1);
chris@3
  1447
      }
chris@3
  1448
      else
chris@3
  1449
      {
chris@3
  1450
        return null;
chris@3
  1451
      }
chris@3
  1452
    }
chris@3
  1453
    catch(SQLException ex)
chris@3
  1454
    {
chris@3
  1455
      restartConnection(ex);
chris@3
  1456
      return getOldestArticle();
chris@3
  1457
    }
chris@3
  1458
    finally
chris@3
  1459
    {
chris@3
  1460
      if(rs != null)
chris@3
  1461
      {
chris@3
  1462
        try
chris@3
  1463
        {
chris@3
  1464
          rs.close();
chris@3
  1465
        }
chris@3
  1466
        catch(SQLException ex)
chris@3
  1467
        {
chris@3
  1468
          ex.printStackTrace();
chris@3
  1469
        }
chris@3
  1470
      }
chris@3
  1471
    }
chris@3
  1472
  }
chris@3
  1473
chris@3
  1474
  @Override
chris@3
  1475
  public int getPostingsCount(String groupname)
chris@3
  1476
    throws StorageBackendException
chris@3
  1477
  {
chris@3
  1478
    ResultSet rs = null;
chris@3
  1479
    
chris@3
  1480
    try
chris@3
  1481
    {
chris@3
  1482
      this.pstmtGetPostingsCount.setString(1, groupname);
chris@3
  1483
      rs = this.pstmtGetPostingsCount.executeQuery();
chris@3
  1484
      if(rs.next())
chris@3
  1485
      {
chris@3
  1486
        return rs.getInt(1);
chris@3
  1487
      }
chris@3
  1488
      else
chris@3
  1489
      {
chris@3
  1490
        Log.msg("Warning: Count on postings return nothing!", true);
chris@3
  1491
        return 0;
chris@3
  1492
      }
chris@3
  1493
    }
chris@3
  1494
    catch(SQLException ex)
chris@3
  1495
    {
chris@3
  1496
      restartConnection(ex);
chris@3
  1497
      return getPostingsCount(groupname);
chris@3
  1498
    }
chris@3
  1499
    finally
chris@3
  1500
    {
chris@3
  1501
      if(rs != null)
chris@3
  1502
      {
chris@3
  1503
        try
chris@3
  1504
        {
chris@3
  1505
          rs.close();
chris@3
  1506
        }
chris@3
  1507
        catch(SQLException ex)
chris@3
  1508
        {
chris@3
  1509
          ex.printStackTrace();
chris@3
  1510
        }
chris@3
  1511
      }
chris@3
  1512
    }
chris@3
  1513
  }
chris@3
  1514
chris@3
  1515
  @Override
chris@3
  1516
  public List<Subscription> getSubscriptions(int feedtype)
chris@3
  1517
    throws StorageBackendException
chris@3
  1518
  {
chris@3
  1519
    ResultSet rs = null;
chris@3
  1520
    
chris@3
  1521
    try
chris@3
  1522
    {
chris@3
  1523
      List<Subscription> subs = new ArrayList<Subscription>();
chris@3
  1524
      this.pstmtGetSubscriptions.setInt(1, feedtype);
chris@3
  1525
      rs = this.pstmtGetSubscriptions.executeQuery();
chris@3
  1526
      
chris@3
  1527
      while(rs.next())
chris@3
  1528
      {
chris@3
  1529
        String host  = rs.getString("host");
chris@3
  1530
        String group = rs.getString("name");
chris@3
  1531
        int    port  = rs.getInt("port");
chris@3
  1532
        subs.add(new Subscription(host, port, feedtype, group));
chris@3
  1533
      }
chris@3
  1534
      
chris@3
  1535
      return subs;
chris@3
  1536
    }
chris@3
  1537
    catch(SQLException ex)
chris@3
  1538
    {
chris@3
  1539
      restartConnection(ex);
chris@3
  1540
      return getSubscriptions(feedtype);
chris@3
  1541
    }
chris@3
  1542
    finally
chris@3
  1543
    {
chris@3
  1544
      if(rs != null)
chris@3
  1545
      {
chris@3
  1546
        try
chris@3
  1547
        {
chris@3
  1548
          rs.close();
chris@3
  1549
        }
chris@3
  1550
        catch(SQLException ex)
chris@3
  1551
        {
chris@3
  1552
          ex.printStackTrace();
chris@3
  1553
        }
chris@3
  1554
      }
chris@3
  1555
    }
chris@3
  1556
  }
chris@3
  1557
chris@3
  1558
  /**
chris@3
  1559
   * Checks if there is an article with the given messageid in the JDBCDatabase.
chris@3
  1560
   * @param name
chris@3
  1561
   * @return
chris@3
  1562
   * @throws StorageBackendException
chris@3
  1563
   */
chris@3
  1564
  @Override
chris@3
  1565
  public boolean isArticleExisting(String messageID)
chris@3
  1566
    throws StorageBackendException
chris@3
  1567
  {
chris@3
  1568
    ResultSet rs = null;
chris@3
  1569
    
chris@3
  1570
    try
chris@3
  1571
    {
chris@3
  1572
      this.pstmtIsArticleExisting.setString(1, messageID);
chris@3
  1573
      rs = this.pstmtIsArticleExisting.executeQuery();
chris@3
  1574
      return rs.next() && rs.getInt(1) == 1;
chris@3
  1575
    }
chris@3
  1576
    catch(SQLException ex)
chris@3
  1577
    {
chris@3
  1578
      restartConnection(ex);
chris@3
  1579
      return isArticleExisting(messageID);
chris@3
  1580
    }
chris@3
  1581
    finally
chris@3
  1582
    {
chris@3
  1583
      if(rs != null)
chris@3
  1584
      {
chris@3
  1585
        try
chris@3
  1586
        {
chris@3
  1587
          rs.close();
chris@3
  1588
        }
chris@3
  1589
        catch(SQLException ex)
chris@3
  1590
        {
chris@3
  1591
          ex.printStackTrace();
chris@3
  1592
        }
chris@3
  1593
      }
chris@3
  1594
    }
chris@3
  1595
  }
chris@3
  1596
  
chris@3
  1597
  /**
chris@3
  1598
   * Checks if there is a group with the given name in the JDBCDatabase.
chris@3
  1599
   * @param name
chris@3
  1600
   * @return
chris@3
  1601
   * @throws StorageBackendException
chris@3
  1602
   */
chris@3
  1603
  @Override
chris@3
  1604
  public boolean isGroupExisting(String name)
chris@3
  1605
    throws StorageBackendException
chris@3
  1606
  {
chris@3
  1607
    ResultSet rs = null;
chris@3
  1608
    
chris@3
  1609
    try
chris@3
  1610
    {
chris@3
  1611
      this.pstmtIsGroupExisting.setString(1, name);
chris@3
  1612
      rs = this.pstmtIsGroupExisting.executeQuery();
chris@3
  1613
      return rs.next();
chris@3
  1614
    }
chris@3
  1615
    catch(SQLException ex)
chris@3
  1616
    {
chris@3
  1617
      restartConnection(ex);
chris@3
  1618
      return isGroupExisting(name);
chris@3
  1619
    }
chris@3
  1620
    finally
chris@3
  1621
    {
chris@3
  1622
      if(rs != null)
chris@3
  1623
      {
chris@3
  1624
        try
chris@3
  1625
        {
chris@3
  1626
          rs.close();
chris@3
  1627
        }
chris@3
  1628
        catch(SQLException ex)
chris@3
  1629
        {
chris@3
  1630
          ex.printStackTrace();
chris@3
  1631
        }
chris@3
  1632
      }
chris@3
  1633
    }
chris@3
  1634
  }
chris@3
  1635
chris@3
  1636
  @Override
chris@3
  1637
  public void setConfigValue(String key, String value)
chris@3
  1638
    throws StorageBackendException
chris@3
  1639
  {
chris@3
  1640
    try
chris@3
  1641
    {
chris@3
  1642
      conn.setAutoCommit(false);
chris@3
  1643
      this.pstmtSetConfigValue0.setString(1, key);
chris@3
  1644
      this.pstmtSetConfigValue0.execute();
chris@3
  1645
      this.pstmtSetConfigValue1.setString(1, key);
chris@3
  1646
      this.pstmtSetConfigValue1.setString(2, value);
chris@3
  1647
      this.pstmtSetConfigValue1.execute();
chris@3
  1648
      conn.commit();
chris@3
  1649
      conn.setAutoCommit(true);
chris@3
  1650
    }
chris@3
  1651
    catch(SQLException ex)
chris@3
  1652
    {
chris@3
  1653
      restartConnection(ex);
chris@3
  1654
      setConfigValue(key, value);
chris@3
  1655
    }
chris@3
  1656
  }
chris@3
  1657
  
chris@3
  1658
  /**
chris@3
  1659
   * Closes the JDBCDatabase connection.
chris@3
  1660
   */
chris@3
  1661
  public void shutdown()
chris@3
  1662
    throws StorageBackendException
chris@3
  1663
  {
chris@3
  1664
    try
chris@3
  1665
    {
chris@3
  1666
      if(this.conn != null)
chris@3
  1667
      {
chris@3
  1668
        this.conn.close();
chris@3
  1669
      }
chris@3
  1670
    }
chris@3
  1671
    catch(SQLException ex)
chris@3
  1672
    {
chris@3
  1673
      throw new StorageBackendException(ex);
chris@3
  1674
    }
chris@3
  1675
  }
chris@3
  1676
chris@3
  1677
  @Override
chris@3
  1678
  public void purgeGroup(Group group)
chris@3
  1679
    throws StorageBackendException
chris@3
  1680
  {
chris@3
  1681
    try
chris@3
  1682
    {
chris@3
  1683
      this.pstmtPurgeGroup0.setLong(1, group.getInternalID());
chris@3
  1684
      this.pstmtPurgeGroup0.executeUpdate();
chris@3
  1685
chris@3
  1686
      this.pstmtPurgeGroup1.setLong(1, group.getInternalID());
chris@3
  1687
      this.pstmtPurgeGroup1.executeUpdate();
chris@3
  1688
    }
chris@3
  1689
    catch(SQLException ex)
chris@3
  1690
    {
chris@3
  1691
      restartConnection(ex);
chris@3
  1692
      purgeGroup(group);
chris@3
  1693
    }
chris@3
  1694
  }
chris@3
  1695
  
chris@3
  1696
  private void restartConnection(SQLException cause)
chris@3
  1697
    throws StorageBackendException
chris@3
  1698
  {
chris@3
  1699
    restarts++;
chris@3
  1700
    Log.msg(Thread.currentThread() 
chris@3
  1701
      + ": Database connection was closed (restart " + restarts + ").", false);
chris@3
  1702
    
chris@3
  1703
    if(restarts >= MAX_RESTARTS)
chris@3
  1704
    {
chris@3
  1705
      // Delete the current, probably broken JDBCDatabase instance.
chris@3
  1706
      // So no one can use the instance any more.
chris@3
  1707
      JDBCDatabaseProvider.instances.remove(Thread.currentThread());
chris@3
  1708
      
chris@3
  1709
      // Throw the exception upwards
chris@3
  1710
      throw new StorageBackendException(cause);
chris@3
  1711
    }
chris@3
  1712
    
chris@3
  1713
    try
chris@3
  1714
    {
chris@3
  1715
      Thread.sleep(1500L * restarts);
chris@3
  1716
    }
chris@3
  1717
    catch(InterruptedException ex)
chris@3
  1718
    {
chris@3
  1719
      Log.msg("Interrupted: " + ex.getMessage(), false);
chris@3
  1720
    }
chris@3
  1721
    
chris@3
  1722
    // Try to properly close the old database connection
chris@3
  1723
    try
chris@3
  1724
    {
chris@3
  1725
      if(this.conn != null)
chris@3
  1726
      {
chris@3
  1727
        this.conn.close();
chris@3
  1728
      }
chris@3
  1729
    }
chris@3
  1730
    catch(SQLException ex)
chris@3
  1731
    {
chris@3
  1732
      Log.msg(ex.getMessage(), true);
chris@3
  1733
    }
chris@3
  1734
    
chris@3
  1735
    try
chris@3
  1736
    {
chris@3
  1737
      // Try to reinitialize database connection
chris@3
  1738
      arise();
chris@3
  1739
    }
chris@3
  1740
    catch(SQLException ex)
chris@3
  1741
    {
chris@3
  1742
      Log.msg(ex.getMessage(), true);
chris@3
  1743
      restartConnection(ex);
chris@3
  1744
    }
chris@3
  1745
  }
chris@3
  1746
chris@3
  1747
  /**
chris@3
  1748
   * Writes the flags and the name of the given group to the database.
chris@3
  1749
   * @param group
chris@3
  1750
   * @throws StorageBackendException
chris@3
  1751
   */
chris@3
  1752
  @Override
chris@3
  1753
  public boolean update(Group group)
chris@3
  1754
    throws StorageBackendException
chris@3
  1755
  {
chris@3
  1756
    try
chris@3
  1757
    {
chris@3
  1758
      this.pstmtUpdateGroup.setInt(1, group.getFlags());
chris@3
  1759
      this.pstmtUpdateGroup.setString(2, group.getName());
chris@3
  1760
      this.pstmtUpdateGroup.setLong(3, group.getInternalID());
chris@3
  1761
      int rs = this.pstmtUpdateGroup.executeUpdate();
chris@3
  1762
      return rs == 1;
chris@3
  1763
    }
chris@3
  1764
    catch(SQLException ex)
chris@3
  1765
    {
chris@3
  1766
      restartConnection(ex);
chris@3
  1767
      return update(group);
chris@3
  1768
    }
chris@3
  1769
  }
chris@3
  1770
chris@3
  1771
}