src/org/sonews/storage/impl/DrupalDatabase.java
author František Kučera <franta-hg@frantovo.cz>
Sat, 05 Nov 2011 00:06:09 +0100
changeset 115 e5bfc969d41f
parent 106 dc04a3c2c557
child 117 79ce65d63cce
permissions -rw-r--r--
SMTP: correct unescaping of posted messages containing lines with single dot.
franta-hg@63
     1
/*
franta-hg@63
     2
 *   SONEWS News Server
franta-hg@63
     3
 *   see AUTHORS for the list of contributors
franta-hg@63
     4
 *
franta-hg@63
     5
 *   This program is free software: you can redistribute it and/or modify
franta-hg@63
     6
 *   it under the terms of the GNU General Public License as published by
franta-hg@63
     7
 *   the Free Software Foundation, either version 3 of the License, or
franta-hg@63
     8
 *   (at your option) any later version.
franta-hg@63
     9
 *
franta-hg@63
    10
 *   This program is distributed in the hope that it will be useful,
franta-hg@63
    11
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
franta-hg@63
    12
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
franta-hg@63
    13
 *   GNU General Public License for more details.
franta-hg@63
    14
 *
franta-hg@63
    15
 *   You should have received a copy of the GNU General Public License
franta-hg@63
    16
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
franta-hg@63
    17
 */
franta-hg@63
    18
package org.sonews.storage.impl;
franta-hg@63
    19
franta-hg@68
    20
import java.sql.Connection;
franta-hg@68
    21
import java.sql.DriverManager;
franta-hg@68
    22
import java.sql.PreparedStatement;
franta-hg@68
    23
import java.sql.ResultSet;
franta-hg@68
    24
import java.sql.Statement;
franta-hg@68
    25
import java.util.ArrayList;
franta-hg@65
    26
import java.util.Collections;
franta-hg@64
    27
import java.util.List;
franta-hg@65
    28
import java.util.logging.Level;
franta-hg@66
    29
import java.util.logging.Logger;
franta-hg@68
    30
import org.sonews.config.Config;
franta-hg@64
    31
import org.sonews.feed.Subscription;
franta-hg@64
    32
import org.sonews.storage.Article;
franta-hg@64
    33
import org.sonews.storage.ArticleHead;
franta-hg@72
    34
import org.sonews.storage.DrupalArticle;
franta-hg@72
    35
import org.sonews.storage.DrupalMessage;
franta-hg@102
    36
import static org.sonews.storage.DrupalMessage.parseArticleID;
franta-hg@102
    37
import static org.sonews.storage.DrupalMessage.parseGroupID;
franta-hg@64
    38
import org.sonews.storage.Group;
franta-hg@64
    39
import org.sonews.storage.Storage;
franta-hg@64
    40
import org.sonews.storage.StorageBackendException;
franta-hg@64
    41
import org.sonews.util.Pair;
franta-hg@64
    42
franta-hg@63
    43
/**
franta-hg@63
    44
 *
franta-hg@63
    45
 * @author František Kučera (frantovo.cz)
franta-hg@63
    46
 */
franta-hg@64
    47
public class DrupalDatabase implements Storage {
franta-hg@67
    48
franta-hg@66
    49
	private static final Logger log = Logger.getLogger(DrupalDatabase.class.getName());
franta-hg@70
    50
	public static final String CHARSET = "UTF-8";
franta-hg@68
    51
	public static final String CRLF = "\r\n";
franta-hg@68
    52
	protected Connection conn = null;
franta-hg@70
    53
	// TODO: správná doména
franta-hg@72
    54
	private String myDomain = "nntp.i1984.cz";
franta-hg@68
    55
franta-hg@68
    56
	public DrupalDatabase() throws StorageBackendException {
franta-hg@68
    57
		connectDatabase();
franta-hg@68
    58
	}
franta-hg@68
    59
franta-hg@68
    60
	private void connectDatabase() throws StorageBackendException {
franta-hg@68
    61
		try {
franta-hg@68
    62
			// Load database driver
franta-hg@68
    63
			String driverClass = Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_DBMSDRIVER, "java.lang.Object");
franta-hg@68
    64
			Class.forName(driverClass);
franta-hg@68
    65
franta-hg@68
    66
			// Establish database connection
franta-hg@68
    67
			String url = Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_DATABASE, "<not specified>");
franta-hg@68
    68
			String username = Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_USER, "root");
franta-hg@68
    69
			String password = Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_PASSWORD, "");
franta-hg@68
    70
			conn = DriverManager.getConnection(url, username, password);
franta-hg@68
    71
franta-hg@102
    72
			/**
franta-hg@102
    73
			 * Kódování češtiny:
franta-hg@102
    74
			 * SET NAMES utf8 → dobrá čeština
franta-hg@102
    75
			 *		Client characterset:    utf8
franta-hg@102
    76
			 *		Conn.  characterset:    utf8
franta-hg@102
    77
			 * SET CHARACTER SET utf8; → dobrá čeština jen pro SLECT, ale při volání funkce se zmrší
franta-hg@102
    78
			 *		Client characterset:    utf8
franta-hg@102
    79
			 *		Conn.  characterset:    latin1
franta-hg@102
    80
			 * 
franta-hg@102
    81
			 * Správné řešení:
franta-hg@102
    82
			 *		V JDBC URL musí být: ?useUnicode=true&characterEncoding=UTF-8
franta-hg@102
    83
			 */
franta-hg@68
    84
			conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
franta-hg@68
    85
			if (conn.getTransactionIsolation() != Connection.TRANSACTION_SERIALIZABLE) {
franta-hg@68
    86
				log.warning("Database is NOT fully serializable!");
franta-hg@68
    87
			}
franta-hg@68
    88
		} catch (Exception e) {
franta-hg@68
    89
			throw new StorageBackendException(e);
franta-hg@68
    90
		}
franta-hg@68
    91
	}
franta-hg@68
    92
franta-hg@68
    93
	protected static void close(Connection connection, Statement statement, ResultSet resultSet) {
franta-hg@68
    94
		if (resultSet != null) {
franta-hg@68
    95
			try {
franta-hg@68
    96
				resultSet.close();
franta-hg@68
    97
			} catch (Exception e) {
franta-hg@68
    98
			}
franta-hg@68
    99
		}
franta-hg@68
   100
		if (statement != null) {
franta-hg@68
   101
			try {
franta-hg@68
   102
				statement.close();
franta-hg@68
   103
			} catch (Exception e) {
franta-hg@68
   104
			}
franta-hg@68
   105
		}
franta-hg@68
   106
		if (connection != null) {
franta-hg@68
   107
			try {
franta-hg@68
   108
				connection.close();
franta-hg@68
   109
			} catch (Exception e) {
franta-hg@68
   110
			}
franta-hg@68
   111
		}
franta-hg@68
   112
	}
franta-hg@64
   113
franta-hg@64
   114
	@Override
franta-hg@67
   115
	public List<Group> getGroups() throws StorageBackendException {
franta-hg@68
   116
		PreparedStatement ps = null;
franta-hg@68
   117
		ResultSet rs = null;
franta-hg@68
   118
		try {
franta-hg@68
   119
			ps = conn.prepareStatement("SELECT * FROM nntp_group");
franta-hg@68
   120
			rs = ps.executeQuery();
franta-hg@68
   121
			List<Group> skupiny = new ArrayList<Group>();
franta-hg@68
   122
franta-hg@68
   123
			while (rs.next()) {
franta-hg@68
   124
				skupiny.add(new Group(rs.getString("name"), rs.getInt("id"), Group.READONLY));
franta-hg@68
   125
			}
franta-hg@68
   126
franta-hg@68
   127
			return skupiny;
franta-hg@68
   128
		} catch (Exception e) {
franta-hg@68
   129
			throw new StorageBackendException(e);
franta-hg@68
   130
		} finally {
franta-hg@68
   131
			close(null, ps, rs);
franta-hg@68
   132
		}
franta-hg@64
   133
	}
franta-hg@64
   134
franta-hg@64
   135
	@Override
franta-hg@67
   136
	public Group getGroup(String name) throws StorageBackendException {
franta-hg@68
   137
		PreparedStatement ps = null;
franta-hg@68
   138
		ResultSet rs = null;
franta-hg@68
   139
		try {
franta-hg@68
   140
			ps = conn.prepareStatement("SELECT * FROM nntp_group WHERE name = ?");
franta-hg@68
   141
			ps.setString(1, name);
franta-hg@68
   142
			rs = ps.executeQuery();
franta-hg@68
   143
franta-hg@68
   144
			while (rs.next()) {
franta-hg@68
   145
				return new Group(rs.getString("name"), rs.getInt("id"), Group.READONLY);
franta-hg@68
   146
			}
franta-hg@68
   147
franta-hg@68
   148
			return null;
franta-hg@68
   149
		} catch (Exception e) {
franta-hg@68
   150
			throw new StorageBackendException(e);
franta-hg@68
   151
		} finally {
franta-hg@68
   152
			close(null, ps, rs);
franta-hg@68
   153
		}
franta-hg@64
   154
	}
franta-hg@64
   155
franta-hg@64
   156
	@Override
franta-hg@67
   157
	public boolean isGroupExisting(String groupname) throws StorageBackendException {
franta-hg@68
   158
		return getGroup(groupname) != null;
franta-hg@64
   159
	}
franta-hg@64
   160
franta-hg@64
   161
	@Override
franta-hg@64
   162
	public Article getArticle(String messageID) throws StorageBackendException {
franta-hg@68
   163
		Long articleID = parseArticleID(messageID);
franta-hg@68
   164
		Long groupID = parseGroupID(messageID);
franta-hg@68
   165
franta-hg@68
   166
		if (articleID == null || groupID == null) {
franta-hg@68
   167
			log.log(Level.SEVERE, "Invalid messageID: {0}", new Object[]{messageID});
franta-hg@68
   168
			return null;
franta-hg@68
   169
		} else {
franta-hg@68
   170
			return getArticle(articleID, groupID);
franta-hg@68
   171
		}
franta-hg@64
   172
	}
franta-hg@64
   173
franta-hg@64
   174
	@Override
franta-hg@68
   175
	public Article getArticle(long articleID, long groupID) throws StorageBackendException {
franta-hg@68
   176
		PreparedStatement ps = null;
franta-hg@68
   177
		ResultSet rs = null;
franta-hg@68
   178
		try {
franta-hg@68
   179
			ps = conn.prepareStatement("SELECT * FROM nntp_article WHERE id = ? AND group_id = ?");
franta-hg@68
   180
			ps.setLong(1, articleID);
franta-hg@68
   181
			ps.setLong(2, groupID);
franta-hg@68
   182
			rs = ps.executeQuery();
franta-hg@68
   183
franta-hg@68
   184
			if (rs.next()) {
franta-hg@72
   185
				DrupalMessage m = new DrupalMessage(rs, myDomain, true);
franta-hg@72
   186
				return new DrupalArticle(m);
franta-hg@68
   187
			} else {
franta-hg@68
   188
				return null;
franta-hg@68
   189
			}
franta-hg@68
   190
		} catch (Exception e) {
franta-hg@68
   191
			throw new StorageBackendException(e);
franta-hg@68
   192
		} finally {
franta-hg@68
   193
			close(null, ps, rs);
franta-hg@68
   194
		}
franta-hg@64
   195
	}
franta-hg@64
   196
franta-hg@64
   197
	@Override
franta-hg@64
   198
	public List<Pair<Long, ArticleHead>> getArticleHeads(Group group, long first, long last) throws StorageBackendException {
franta-hg@68
   199
		PreparedStatement ps = null;
franta-hg@68
   200
		ResultSet rs = null;
franta-hg@68
   201
		try {
franta-hg@70
   202
			ps = conn.prepareStatement("SELECT * FROM nntp_article WHERE group_id = ? AND id >= ? AND id <= ? ORDER BY id");
franta-hg@68
   203
			ps.setLong(1, group.getInternalID());
franta-hg@68
   204
			ps.setLong(2, first);
franta-hg@68
   205
			ps.setLong(3, last);
franta-hg@68
   206
			rs = ps.executeQuery();
franta-hg@68
   207
franta-hg@68
   208
			List<Pair<Long, ArticleHead>> heads = new ArrayList<Pair<Long, ArticleHead>>();
franta-hg@68
   209
franta-hg@68
   210
			while (rs.next()) {
franta-hg@72
   211
				DrupalMessage m = new DrupalMessage(rs, myDomain, false);
franta-hg@72
   212
				String headers = m.getHeaders();
franta-hg@68
   213
				heads.add(new Pair<Long, ArticleHead>(rs.getLong("id"), new ArticleHead(headers)));
franta-hg@68
   214
			}
franta-hg@68
   215
franta-hg@68
   216
			return heads;
franta-hg@68
   217
		} catch (Exception e) {
franta-hg@68
   218
			throw new StorageBackendException(e);
franta-hg@68
   219
		} finally {
franta-hg@68
   220
			close(null, ps, rs);
franta-hg@68
   221
		}
franta-hg@64
   222
	}
franta-hg@64
   223
franta-hg@64
   224
	@Override
franta-hg@68
   225
	public long getArticleIndex(Article article, Group group) throws StorageBackendException {
franta-hg@68
   226
		Long id = parseArticleID(article.getMessageID());
franta-hg@68
   227
		if (id == null) {
franta-hg@68
   228
			throw new StorageBackendException("Invalid messageID: " + article.getMessageID());
franta-hg@68
   229
		} else {
franta-hg@68
   230
			return id;
franta-hg@68
   231
		}
franta-hg@64
   232
	}
franta-hg@64
   233
franta-hg@64
   234
	@Override
franta-hg@64
   235
	public List<Long> getArticleNumbers(long groupID) throws StorageBackendException {
franta-hg@68
   236
		PreparedStatement ps = null;
franta-hg@68
   237
		ResultSet rs = null;
franta-hg@68
   238
		try {
franta-hg@68
   239
			ps = conn.prepareStatement("SELECT id FROM nntp_article WHERE group_id = ?");
franta-hg@68
   240
			ps.setLong(1, groupID);
franta-hg@68
   241
			rs = ps.executeQuery();
franta-hg@68
   242
			List<Long> articleNumbers = new ArrayList<Long>();
franta-hg@68
   243
			while (rs.next()) {
franta-hg@68
   244
				articleNumbers.add(rs.getLong(1));
franta-hg@68
   245
			}
franta-hg@68
   246
			return articleNumbers;
franta-hg@68
   247
		} catch (Exception e) {
franta-hg@68
   248
			throw new StorageBackendException(e);
franta-hg@68
   249
		} finally {
franta-hg@68
   250
			close(null, ps, rs);
franta-hg@68
   251
		}
franta-hg@64
   252
	}
franta-hg@64
   253
franta-hg@64
   254
	@Override
franta-hg@67
   255
	public int getFirstArticleNumber(Group group) throws StorageBackendException {
franta-hg@68
   256
		PreparedStatement ps = null;
franta-hg@68
   257
		ResultSet rs = null;
franta-hg@68
   258
		try {
franta-hg@68
   259
			ps = conn.prepareStatement("SELECT min(id) FROM nntp_article WHERE group_id = ?");
franta-hg@68
   260
			ps.setLong(1, group.getInternalID());
franta-hg@68
   261
			rs = ps.executeQuery();
franta-hg@68
   262
			rs.next();
franta-hg@68
   263
			return rs.getInt(1);
franta-hg@68
   264
		} catch (Exception e) {
franta-hg@68
   265
			throw new StorageBackendException(e);
franta-hg@68
   266
		} finally {
franta-hg@68
   267
			close(null, ps, rs);
franta-hg@68
   268
		}
franta-hg@67
   269
	}
franta-hg@67
   270
franta-hg@67
   271
	@Override
franta-hg@67
   272
	public int getLastArticleNumber(Group group) throws StorageBackendException {
franta-hg@68
   273
		PreparedStatement ps = null;
franta-hg@68
   274
		ResultSet rs = null;
franta-hg@68
   275
		try {
franta-hg@68
   276
			ps = conn.prepareStatement("SELECT max(id) FROM nntp_article WHERE group_id = ?");
franta-hg@68
   277
			ps.setLong(1, group.getInternalID());
franta-hg@68
   278
			rs = ps.executeQuery();
franta-hg@68
   279
			rs.next();
franta-hg@68
   280
			return rs.getInt(1);
franta-hg@68
   281
		} catch (Exception e) {
franta-hg@68
   282
			throw new StorageBackendException(e);
franta-hg@68
   283
		} finally {
franta-hg@68
   284
			close(null, ps, rs);
franta-hg@68
   285
		}
franta-hg@67
   286
	}
franta-hg@67
   287
franta-hg@67
   288
	@Override
franta-hg@67
   289
	public boolean isArticleExisting(String messageID) throws StorageBackendException {
franta-hg@68
   290
		Long articleID = parseArticleID(messageID);
franta-hg@68
   291
		Long groupID = parseGroupID(messageID);
franta-hg@68
   292
franta-hg@68
   293
		if (articleID == null || groupID == null) {
franta-hg@68
   294
			return false;
franta-hg@68
   295
		} else {
franta-hg@68
   296
			PreparedStatement ps = null;
franta-hg@68
   297
			ResultSet rs = null;
franta-hg@68
   298
			try {
franta-hg@68
   299
				ps = conn.prepareStatement("SELECT count(*) FROM nntp_article WHERE id = ? AND group_id = ?");
franta-hg@68
   300
				ps.setLong(1, articleID);
franta-hg@68
   301
				ps.setLong(2, groupID);
franta-hg@68
   302
				rs = ps.executeQuery();
franta-hg@68
   303
franta-hg@68
   304
				rs.next();
franta-hg@68
   305
				return rs.getInt(1) == 1;
franta-hg@68
   306
			} catch (Exception e) {
franta-hg@68
   307
				throw new StorageBackendException(e);
franta-hg@68
   308
			} finally {
franta-hg@68
   309
				close(null, ps, rs);
franta-hg@68
   310
			}
franta-hg@68
   311
		}
franta-hg@67
   312
	}
franta-hg@67
   313
franta-hg@67
   314
	@Override
franta-hg@67
   315
	public int countArticles() throws StorageBackendException {
franta-hg@69
   316
		PreparedStatement ps = null;
franta-hg@69
   317
		ResultSet rs = null;
franta-hg@69
   318
		try {
franta-hg@69
   319
			ps = conn.prepareStatement("SELECT count(*) FROM nntp_article");
franta-hg@69
   320
			rs = ps.executeQuery();
franta-hg@69
   321
			rs.next();
franta-hg@69
   322
			return rs.getInt(1);
franta-hg@69
   323
		} catch (Exception e) {
franta-hg@69
   324
			throw new StorageBackendException(e);
franta-hg@69
   325
		} finally {
franta-hg@69
   326
			close(null, ps, rs);
franta-hg@69
   327
		}
franta-hg@67
   328
	}
franta-hg@67
   329
franta-hg@67
   330
	@Override
franta-hg@67
   331
	public int countGroups() throws StorageBackendException {
franta-hg@69
   332
		PreparedStatement ps = null;
franta-hg@69
   333
		ResultSet rs = null;
franta-hg@69
   334
		try {
franta-hg@69
   335
			ps = conn.prepareStatement("SELECT count(*) FROM nntp_group");
franta-hg@69
   336
			rs = ps.executeQuery();
franta-hg@69
   337
			rs.next();
franta-hg@69
   338
			return rs.getInt(1);
franta-hg@69
   339
		} catch (Exception e) {
franta-hg@69
   340
			throw new StorageBackendException(e);
franta-hg@69
   341
		} finally {
franta-hg@69
   342
			close(null, ps, rs);
franta-hg@69
   343
		}
franta-hg@67
   344
	}
franta-hg@67
   345
franta-hg@67
   346
	@Override
franta-hg@72
   347
	public int getPostingsCount(String groupname) throws StorageBackendException {
franta-hg@72
   348
		PreparedStatement ps = null;
franta-hg@72
   349
		ResultSet rs = null;
franta-hg@72
   350
		try {
franta-hg@72
   351
			ps = conn.prepareStatement("SELECT count(*) FROM nntp_article WHERE group_name = ?");
franta-hg@72
   352
			ps.setString(1, groupname);
franta-hg@72
   353
			rs = ps.executeQuery();
franta-hg@72
   354
			rs.next();
franta-hg@72
   355
			return rs.getInt(1);
franta-hg@72
   356
		} catch (Exception e) {
franta-hg@72
   357
			throw new StorageBackendException(e);
franta-hg@72
   358
		} finally {
franta-hg@72
   359
			close(null, ps, rs);
franta-hg@72
   360
		}
franta-hg@72
   361
	}
franta-hg@72
   362
franta-hg@72
   363
	@Override
franta-hg@72
   364
	public List<Pair<Long, String>> getArticleHeaders(Group group, long start, long end, String header, String pattern) throws StorageBackendException {
franta-hg@72
   365
		log.log(Level.SEVERE, "TODO: getArticleHeaders {0} / {1} / {2} / {3} / {4}", new Object[]{group, start, end, header, pattern});
franta-hg@72
   366
		/** TODO: */
franta-hg@72
   367
		return Collections.emptyList();
franta-hg@72
   368
	}
franta-hg@72
   369
franta-hg@101
   370
	/**
franta-hg@101
   371
	 * Checks username and password.
franta-hg@101
   372
	 * @param username
franta-hg@101
   373
	 * @param password
franta-hg@101
   374
	 * @return true if credentials are valid | false otherwise
franta-hg@101
   375
	 * @throws StorageBackendException it there is any error during authentication process 
franta-hg@101
   376
	 * (but should not be thrown if only bad thing is wrong username or password)
franta-hg@101
   377
	 */
franta-hg@101
   378
	@Override
franta-hg@102
   379
	public boolean authenticateUser(String username,
franta-hg@102
   380
			char[] password) throws StorageBackendException {
franta-hg@101
   381
		PreparedStatement ps = null;
franta-hg@101
   382
		ResultSet rs = null;
franta-hg@101
   383
		try {
franta-hg@101
   384
			ps = conn.prepareStatement("SELECT nntp_login(?, ?)");
franta-hg@101
   385
			ps.setString(1, username);
franta-hg@101
   386
			ps.setString(2, String.copyValueOf(password));
franta-hg@101
   387
			rs = ps.executeQuery();
franta-hg@101
   388
			rs.next();
franta-hg@101
   389
			return rs.getInt(1) == 1;
franta-hg@101
   390
		} catch (Exception e) {
franta-hg@101
   391
			throw new StorageBackendException(e);
franta-hg@101
   392
		} finally {
franta-hg@101
   393
			close(null, ps, rs);
franta-hg@101
   394
		}
franta-hg@101
   395
	}
franta-hg@101
   396
franta-hg@102
   397
	/**
franta-hg@102
   398
	 * Validates article and if OK, calls {@link #insertArticle(java.lang.String, java.lang.String, java.lang.String, java.lang.Long, java.lang.Long) }
franta-hg@102
   399
	 * @param article
franta-hg@102
   400
	 * @throws StorageBackendException 
franta-hg@102
   401
	 */
franta-hg@72
   402
	@Override
franta-hg@102
   403
	public void addArticle(Article article) throws StorageBackendException {
franta-hg@102
   404
		if (article.getAuthenticatedUser() == null) {
franta-hg@101
   405
			log.log(Level.SEVERE, "User was not authenticated, so his article was rejected.");
franta-hg@101
   406
			throw new StorageBackendException("User must be authenticated to post articles");
franta-hg@101
   407
		} else {
franta-hg@102
   408
			try {
franta-hg@102
   409
				DrupalMessage m = new DrupalMessage(article);
franta-hg@101
   410
franta-hg@102
   411
				Long parentID = m.getParentID();
franta-hg@102
   412
				Long groupID = m.getGroupID();
franta-hg@102
   413
franta-hg@102
   414
				if (parentID == null || groupID == null) {
franta-hg@102
   415
					throw new StorageBackendException("No valid In-Reply-To header was found → rejecting posted message.");
franta-hg@102
   416
				} else {
franta-hg@102
   417
franta-hg@103
   418
					String subject = m.getSubject();
franta-hg@103
   419
					String text = m.getBodyXhtmlFragment();
franta-hg@102
   420
franta-hg@103
   421
					if (subject == null || subject.length() < 1) {
franta-hg@106
   422
						String plainText = m.getBodyPlainText();
franta-hg@106
   423
						subject = plainText.substring(0, Math.min(32, plainText.length()));
franta-hg@106
   424
						if (subject.length() < plainText.length()) {
franta-hg@106
   425
							subject = subject + "…";
franta-hg@106
   426
						}
franta-hg@102
   427
					}
franta-hg@103
   428
franta-hg@103
   429
					insertArticle(article.getAuthenticatedUser(), subject, text, parentID, groupID);
franta-hg@103
   430
					log.log(Level.INFO, "User ''{0}'' has posted an article", article.getAuthenticatedUser());
franta-hg@102
   431
				}
franta-hg@102
   432
			} catch (Exception e) {
franta-hg@102
   433
				throw new StorageBackendException(e);
franta-hg@102
   434
			}
franta-hg@102
   435
		}
franta-hg@102
   436
	}
franta-hg@102
   437
franta-hg@102
   438
	/**
franta-hg@102
   439
	 * Physically stores article in database.
franta-hg@102
   440
	 * @param subject
franta-hg@102
   441
	 * @param text
franta-hg@102
   442
	 * @param parentID
franta-hg@102
   443
	 * @param groupID 
franta-hg@102
   444
	 */
franta-hg@102
   445
	private void insertArticle(String sender, String subject, String text, Long parentID, Long groupID) throws StorageBackendException {
franta-hg@102
   446
		PreparedStatement ps = null;
franta-hg@102
   447
		ResultSet rs = null;
franta-hg@102
   448
		try {
franta-hg@102
   449
			ps = conn.prepareStatement("SELECT nntp_post_article(?, ?, ?, ?, ?)");
franta-hg@102
   450
franta-hg@102
   451
			ps.setString(1, sender);
franta-hg@102
   452
			ps.setString(2, subject);
franta-hg@102
   453
			ps.setString(3, text);
franta-hg@102
   454
			ps.setLong(4, parentID);
franta-hg@102
   455
			ps.setLong(5, groupID);
franta-hg@102
   456
franta-hg@102
   457
			rs = ps.executeQuery();
franta-hg@102
   458
			rs.next();
franta-hg@102
   459
franta-hg@102
   460
			Long articleID = rs.getLong(1);
franta-hg@102
   461
			log.log(Level.INFO, "Article was succesfully stored as {0}", articleID);
franta-hg@102
   462
		} catch (Exception e) {
franta-hg@102
   463
			throw new StorageBackendException(e);
franta-hg@102
   464
		} finally {
franta-hg@102
   465
			close(null, ps, rs);
franta-hg@101
   466
		}
franta-hg@72
   467
	}
franta-hg@72
   468
franta-hg@72
   469
	@Override
franta-hg@72
   470
	public void addEvent(long timestamp, int type, long groupID) throws StorageBackendException {
franta-hg@72
   471
		log.log(Level.SEVERE, "TODO: addEvent {0} / {1} / {2}", new Object[]{timestamp, type, groupID});
franta-hg@72
   472
	}
franta-hg@72
   473
franta-hg@72
   474
	@Override
franta-hg@72
   475
	public void addGroup(String groupname, int flags) throws StorageBackendException {
franta-hg@72
   476
		log.log(Level.SEVERE, "TODO: addGroup {0} / {1}", new Object[]{groupname, flags});
franta-hg@72
   477
	}
franta-hg@72
   478
franta-hg@72
   479
	@Override
franta-hg@67
   480
	public void delete(String messageID) throws StorageBackendException {
franta-hg@67
   481
		log.log(Level.SEVERE, "TODO: delete {0}", new Object[]{messageID});
franta-hg@67
   482
	}
franta-hg@67
   483
franta-hg@67
   484
	@Override
franta-hg@64
   485
	public String getConfigValue(String key) throws StorageBackendException {
franta-hg@69
   486
		//log.log(Level.SEVERE, "TODO: getConfigValue {0}", new Object[]{key});
franta-hg@65
   487
		return null;
franta-hg@64
   488
	}
franta-hg@64
   489
franta-hg@64
   490
	@Override
franta-hg@72
   491
	public void setConfigValue(String key, String value) throws StorageBackendException {
franta-hg@72
   492
		log.log(Level.SEVERE, "TODO: setConfigValue {0} = {1}", new Object[]{key, value});
franta-hg@72
   493
	}
franta-hg@72
   494
franta-hg@72
   495
	@Override
franta-hg@64
   496
	public int getEventsCount(int eventType, long startTimestamp, long endTimestamp, Group group) throws StorageBackendException {
franta-hg@66
   497
		log.log(Level.SEVERE, "TODO: getEventsCount {0} / {1} / {2} / {3}", new Object[]{eventType, startTimestamp, endTimestamp, group});
franta-hg@65
   498
		return 0;
franta-hg@64
   499
	}
franta-hg@64
   500
franta-hg@64
   501
	@Override
franta-hg@64
   502
	public double getEventsPerHour(int key, long gid) throws StorageBackendException {
franta-hg@66
   503
		log.log(Level.SEVERE, "TODO: getEventsPerHour {0} / {1}", new Object[]{key, gid});
franta-hg@65
   504
		return 0;
franta-hg@64
   505
	}
franta-hg@64
   506
franta-hg@64
   507
	@Override
franta-hg@64
   508
	public List<String> getGroupsForList(String listAddress) throws StorageBackendException {
franta-hg@66
   509
		log.log(Level.SEVERE, "TODO: getGroupsForList {0}", new Object[]{listAddress});
franta-hg@65
   510
		return Collections.emptyList();
franta-hg@64
   511
	}
franta-hg@64
   512
franta-hg@64
   513
	@Override
franta-hg@64
   514
	public List<String> getListsForGroup(String groupname) throws StorageBackendException {
franta-hg@66
   515
		log.log(Level.SEVERE, "TODO: getListsForGroup {0}", new Object[]{groupname});
franta-hg@65
   516
		return Collections.emptyList();
franta-hg@64
   517
	}
franta-hg@64
   518
franta-hg@64
   519
	@Override
franta-hg@64
   520
	public String getOldestArticle() throws StorageBackendException {
franta-hg@66
   521
		log.log(Level.SEVERE, "TODO: getOldestArticle");
franta-hg@65
   522
		return null;
franta-hg@64
   523
	}
franta-hg@64
   524
franta-hg@64
   525
	@Override
franta-hg@64
   526
	public List<Subscription> getSubscriptions(int type) throws StorageBackendException {
franta-hg@66
   527
		log.log(Level.SEVERE, "TODO: getSubscriptions {0}", new Object[]{type});
franta-hg@65
   528
		return Collections.emptyList();
franta-hg@64
   529
	}
franta-hg@64
   530
franta-hg@64
   531
	@Override
franta-hg@64
   532
	public void purgeGroup(Group group) throws StorageBackendException {
franta-hg@66
   533
		log.log(Level.SEVERE, "TODO: purgeGroup {0}", new Object[]{group});
franta-hg@64
   534
	}
franta-hg@64
   535
franta-hg@64
   536
	@Override
franta-hg@64
   537
	public boolean update(Article article) throws StorageBackendException {
franta-hg@66
   538
		log.log(Level.SEVERE, "TODO: update {0}", new Object[]{article});
franta-hg@65
   539
		throw new StorageBackendException("Not implemented yet.");
franta-hg@64
   540
	}
franta-hg@64
   541
franta-hg@64
   542
	@Override
franta-hg@64
   543
	public boolean update(Group group) throws StorageBackendException {
franta-hg@66
   544
		log.log(Level.SEVERE, "TODO: update {0}", new Object[]{group});
franta-hg@65
   545
		throw new StorageBackendException("Not implemented yet.");
franta-hg@64
   546
	}
franta-hg@63
   547
}