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