src/org/sonews/storage/impl/DrupalDatabase.java
author František Kučera <franta-hg@frantovo.cz>
Wed Oct 12 00:11:25 2011 +0200 (2011-10-12)
changeset 72 aae4b4688700
parent 71 beb11d70f0eb
child 73 1feed5fbf147
permissions -rw-r--r--
Drupal: zprávy jsou multipart/alternative – jak prostý text, tak XHTML. TODO: filtry prostého textu a XHTML
zatím se do všech zpráv vkládá stejný vycpávkový text.
     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.text.SimpleDateFormat;
    26 import java.util.ArrayList;
    27 import java.util.Collections;
    28 import java.util.List;
    29 import java.util.Locale;
    30 import java.util.logging.Level;
    31 import java.util.logging.Logger;
    32 import javax.mail.Message;
    33 import org.apache.commons.codec.net.QuotedPrintableCodec;
    34 import org.sonews.config.Config;
    35 import org.sonews.feed.Subscription;
    36 import org.sonews.storage.Article;
    37 import org.sonews.storage.ArticleHead;
    38 import org.sonews.storage.DrupalArticle;
    39 import org.sonews.storage.DrupalMessage;
    40 import org.sonews.storage.Group;
    41 import org.sonews.storage.Storage;
    42 import org.sonews.storage.StorageBackendException;
    43 import org.sonews.util.Pair;
    44 
    45 /**
    46  *
    47  * @author František Kučera (frantovo.cz)
    48  */
    49 public class DrupalDatabase implements Storage {
    50 
    51 	private static final Logger log = Logger.getLogger(DrupalDatabase.class.getName());
    52 	public static final String CHARSET = "UTF-8";
    53 	public static final String CRLF = "\r\n";
    54 	protected Connection conn = null;
    55 	private QuotedPrintableCodec qpc = new QuotedPrintableCodec(CHARSET);
    56 	private SimpleDateFormat RFC822_DATE = new SimpleDateFormat("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z", Locale.US);
    57 	// TODO: správná doména
    58 	private String myDomain = "nntp.i1984.cz";
    59 
    60 	public DrupalDatabase() throws StorageBackendException {
    61 		connectDatabase();
    62 	}
    63 
    64 	private void connectDatabase() throws StorageBackendException {
    65 		try {
    66 			// Load database driver
    67 			String driverClass = Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_DBMSDRIVER, "java.lang.Object");
    68 			Class.forName(driverClass);
    69 
    70 			// Establish database connection
    71 			String url = Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_DATABASE, "<not specified>");
    72 			String username = Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_USER, "root");
    73 			String password = Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_PASSWORD, "");
    74 			conn = DriverManager.getConnection(url, username, password);
    75 
    76 			conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
    77 			if (conn.getTransactionIsolation() != Connection.TRANSACTION_SERIALIZABLE) {
    78 				log.warning("Database is NOT fully serializable!");
    79 			}
    80 		} catch (Exception e) {
    81 			throw new StorageBackendException(e);
    82 		}
    83 	}
    84 
    85 	protected static void close(Connection connection, Statement statement, ResultSet resultSet) {
    86 		if (resultSet != null) {
    87 			try {
    88 				resultSet.close();
    89 			} catch (Exception e) {
    90 			}
    91 		}
    92 		if (statement != null) {
    93 			try {
    94 				statement.close();
    95 			} catch (Exception e) {
    96 			}
    97 		}
    98 		if (connection != null) {
    99 			try {
   100 				connection.close();
   101 			} catch (Exception e) {
   102 			}
   103 		}
   104 	}
   105 
   106 	/**
   107 	 * 
   108 	 * @param messageID &lt;{0}-{1}-{2}@domain.tld&gt; where {0} is nntp_id and {1} is group_id and {2} is group_name
   109 	 * @return array where [0] = nntp_id and [1] = group_id and [2] = group_name or returns null if messageID is invalid
   110 	 */
   111 	private static String[] parseMessageID(String messageID) {
   112 		if (messageID.matches("<[0-9]+\\-[0-9]+\\-[a-z0-9\\.]+@.+>")) {
   113 			return messageID.substring(1).split("@")[0].split("\\-");
   114 		} else {
   115 			return null;
   116 		}
   117 	}
   118 
   119 	private static Long parseArticleID(String messageID) {
   120 		String[] localPart = parseMessageID(messageID);
   121 		if (localPart == null) {
   122 			return null;
   123 		} else {
   124 			return Long.parseLong(localPart[0]);
   125 		}
   126 	}
   127 
   128 	private static Long parseGroupID(String messageID) {
   129 		String[] localPart = parseMessageID(messageID);
   130 		if (localPart == null) {
   131 			return null;
   132 		} else {
   133 			return Long.parseLong(localPart[1]);
   134 			// If needed:
   135 			// parseGroupName() will be same as this method, just with:
   136 			// return localPart[2];
   137 		}
   138 	}
   139 
   140 	@Override
   141 	public List<Group> getGroups() throws StorageBackendException {
   142 		PreparedStatement ps = null;
   143 		ResultSet rs = null;
   144 		try {
   145 			ps = conn.prepareStatement("SELECT * FROM nntp_group");
   146 			rs = ps.executeQuery();
   147 			List<Group> skupiny = new ArrayList<Group>();
   148 
   149 			while (rs.next()) {
   150 				skupiny.add(new Group(rs.getString("name"), rs.getInt("id"), Group.READONLY));
   151 			}
   152 
   153 			return skupiny;
   154 		} catch (Exception e) {
   155 			throw new StorageBackendException(e);
   156 		} finally {
   157 			close(null, ps, rs);
   158 		}
   159 	}
   160 
   161 	@Override
   162 	public Group getGroup(String name) throws StorageBackendException {
   163 		PreparedStatement ps = null;
   164 		ResultSet rs = null;
   165 		try {
   166 			ps = conn.prepareStatement("SELECT * FROM nntp_group WHERE name = ?");
   167 			ps.setString(1, name);
   168 			rs = ps.executeQuery();
   169 
   170 			while (rs.next()) {
   171 				return new Group(rs.getString("name"), rs.getInt("id"), Group.READONLY);
   172 			}
   173 
   174 			return null;
   175 		} catch (Exception e) {
   176 			throw new StorageBackendException(e);
   177 		} finally {
   178 			close(null, ps, rs);
   179 		}
   180 	}
   181 
   182 	@Override
   183 	public boolean isGroupExisting(String groupname) throws StorageBackendException {
   184 		return getGroup(groupname) != null;
   185 	}
   186 
   187 	@Override
   188 	public Article getArticle(String messageID) throws StorageBackendException {
   189 		Long articleID = parseArticleID(messageID);
   190 		Long groupID = parseGroupID(messageID);
   191 
   192 		if (articleID == null || groupID == null) {
   193 			log.log(Level.SEVERE, "Invalid messageID: {0}", new Object[]{messageID});
   194 			return null;
   195 		} else {
   196 			return getArticle(articleID, groupID);
   197 		}
   198 	}
   199 
   200 	@Override
   201 	public Article getArticle(long articleID, long groupID) throws StorageBackendException {
   202 		PreparedStatement ps = null;
   203 		ResultSet rs = null;
   204 		try {
   205 			ps = conn.prepareStatement("SELECT * FROM nntp_article WHERE id = ? AND group_id = ?");
   206 			ps.setLong(1, articleID);
   207 			ps.setLong(2, groupID);
   208 			rs = ps.executeQuery();
   209 
   210 			if (rs.next()) {
   211 				DrupalMessage m = new DrupalMessage(rs, myDomain, true);
   212 				return new DrupalArticle(m);
   213 			} else {
   214 				return null;
   215 			}
   216 		} catch (Exception e) {
   217 			throw new StorageBackendException(e);
   218 		} finally {
   219 			close(null, ps, rs);
   220 		}
   221 	}
   222 
   223 	@Override
   224 	public List<Pair<Long, ArticleHead>> getArticleHeads(Group group, long first, long last) throws StorageBackendException {
   225 		PreparedStatement ps = null;
   226 		ResultSet rs = null;
   227 		try {
   228 			ps = conn.prepareStatement("SELECT * FROM nntp_article WHERE group_id = ? AND id >= ? AND id <= ? ORDER BY id");
   229 			ps.setLong(1, group.getInternalID());
   230 			ps.setLong(2, first);
   231 			ps.setLong(3, last);
   232 			rs = ps.executeQuery();
   233 
   234 			List<Pair<Long, ArticleHead>> heads = new ArrayList<Pair<Long, ArticleHead>>();
   235 
   236 			while (rs.next()) {
   237 				DrupalMessage m = new DrupalMessage(rs, myDomain, false);
   238 				String headers = m.getHeaders();
   239 				heads.add(new Pair<Long, ArticleHead>(rs.getLong("id"), new ArticleHead(headers)));
   240 			}
   241 
   242 			return heads;
   243 		} catch (Exception e) {
   244 			throw new StorageBackendException(e);
   245 		} finally {
   246 			close(null, ps, rs);
   247 		}
   248 	}
   249 
   250 	@Override
   251 	public long getArticleIndex(Article article, Group group) throws StorageBackendException {
   252 		Long id = parseArticleID(article.getMessageID());
   253 		if (id == null) {
   254 			throw new StorageBackendException("Invalid messageID: " + article.getMessageID());
   255 		} else {
   256 			return id;
   257 		}
   258 	}
   259 
   260 	@Override
   261 	public List<Long> getArticleNumbers(long groupID) throws StorageBackendException {
   262 		PreparedStatement ps = null;
   263 		ResultSet rs = null;
   264 		try {
   265 			ps = conn.prepareStatement("SELECT id FROM nntp_article WHERE group_id = ?");
   266 			ps.setLong(1, groupID);
   267 			rs = ps.executeQuery();
   268 			List<Long> articleNumbers = new ArrayList<Long>();
   269 			while (rs.next()) {
   270 				articleNumbers.add(rs.getLong(1));
   271 			}
   272 			return articleNumbers;
   273 		} catch (Exception e) {
   274 			throw new StorageBackendException(e);
   275 		} finally {
   276 			close(null, ps, rs);
   277 		}
   278 	}
   279 
   280 	@Override
   281 	public int getFirstArticleNumber(Group group) throws StorageBackendException {
   282 		PreparedStatement ps = null;
   283 		ResultSet rs = null;
   284 		try {
   285 			ps = conn.prepareStatement("SELECT min(id) FROM nntp_article WHERE group_id = ?");
   286 			ps.setLong(1, group.getInternalID());
   287 			rs = ps.executeQuery();
   288 			rs.next();
   289 			return rs.getInt(1);
   290 		} catch (Exception e) {
   291 			throw new StorageBackendException(e);
   292 		} finally {
   293 			close(null, ps, rs);
   294 		}
   295 	}
   296 
   297 	@Override
   298 	public int getLastArticleNumber(Group group) throws StorageBackendException {
   299 		PreparedStatement ps = null;
   300 		ResultSet rs = null;
   301 		try {
   302 			ps = conn.prepareStatement("SELECT max(id) FROM nntp_article WHERE group_id = ?");
   303 			ps.setLong(1, group.getInternalID());
   304 			rs = ps.executeQuery();
   305 			rs.next();
   306 			return rs.getInt(1);
   307 		} catch (Exception e) {
   308 			throw new StorageBackendException(e);
   309 		} finally {
   310 			close(null, ps, rs);
   311 		}
   312 	}
   313 
   314 	@Override
   315 	public boolean isArticleExisting(String messageID) throws StorageBackendException {
   316 		Long articleID = parseArticleID(messageID);
   317 		Long groupID = parseGroupID(messageID);
   318 
   319 		if (articleID == null || groupID == null) {
   320 			return false;
   321 		} else {
   322 			PreparedStatement ps = null;
   323 			ResultSet rs = null;
   324 			try {
   325 				ps = conn.prepareStatement("SELECT count(*) FROM nntp_article WHERE id = ? AND group_id = ?");
   326 				ps.setLong(1, articleID);
   327 				ps.setLong(2, groupID);
   328 				rs = ps.executeQuery();
   329 
   330 				rs.next();
   331 				return rs.getInt(1) == 1;
   332 			} catch (Exception e) {
   333 				throw new StorageBackendException(e);
   334 			} finally {
   335 				close(null, ps, rs);
   336 			}
   337 		}
   338 	}
   339 
   340 	@Override
   341 	public int countArticles() throws StorageBackendException {
   342 		PreparedStatement ps = null;
   343 		ResultSet rs = null;
   344 		try {
   345 			ps = conn.prepareStatement("SELECT count(*) FROM nntp_article");
   346 			rs = ps.executeQuery();
   347 			rs.next();
   348 			return rs.getInt(1);
   349 		} catch (Exception e) {
   350 			throw new StorageBackendException(e);
   351 		} finally {
   352 			close(null, ps, rs);
   353 		}
   354 	}
   355 
   356 	@Override
   357 	public int countGroups() throws StorageBackendException {
   358 		PreparedStatement ps = null;
   359 		ResultSet rs = null;
   360 		try {
   361 			ps = conn.prepareStatement("SELECT count(*) FROM nntp_group");
   362 			rs = ps.executeQuery();
   363 			rs.next();
   364 			return rs.getInt(1);
   365 		} catch (Exception e) {
   366 			throw new StorageBackendException(e);
   367 		} finally {
   368 			close(null, ps, rs);
   369 		}
   370 	}
   371 
   372 	@Override
   373 	public int getPostingsCount(String groupname) throws StorageBackendException {
   374 		PreparedStatement ps = null;
   375 		ResultSet rs = null;
   376 		try {
   377 			ps = conn.prepareStatement("SELECT count(*) FROM nntp_article WHERE group_name = ?");
   378 			ps.setString(1, groupname);
   379 			rs = ps.executeQuery();
   380 			rs.next();
   381 			return rs.getInt(1);
   382 		} catch (Exception e) {
   383 			throw new StorageBackendException(e);
   384 		} finally {
   385 			close(null, ps, rs);
   386 		}
   387 	}
   388 
   389 	@Override
   390 	public List<Pair<Long, String>> getArticleHeaders(Group group, long start, long end, String header, String pattern) throws StorageBackendException {
   391 		log.log(Level.SEVERE, "TODO: getArticleHeaders {0} / {1} / {2} / {3} / {4}", new Object[]{group, start, end, header, pattern});
   392 		/** TODO: */
   393 		return Collections.emptyList();
   394 	}
   395 
   396 	@Override
   397 	public void addArticle(Article art) throws StorageBackendException {
   398 		log.log(Level.SEVERE, "TODO: addArticle {0}", new Object[]{art});
   399 	}
   400 
   401 	@Override
   402 	public void addEvent(long timestamp, int type, long groupID) throws StorageBackendException {
   403 		log.log(Level.SEVERE, "TODO: addEvent {0} / {1} / {2}", new Object[]{timestamp, type, groupID});
   404 	}
   405 
   406 	@Override
   407 	public void addGroup(String groupname, int flags) throws StorageBackendException {
   408 		log.log(Level.SEVERE, "TODO: addGroup {0} / {1}", new Object[]{groupname, flags});
   409 	}
   410 
   411 	@Override
   412 	public void delete(String messageID) throws StorageBackendException {
   413 		log.log(Level.SEVERE, "TODO: delete {0}", new Object[]{messageID});
   414 	}
   415 
   416 	@Override
   417 	public String getConfigValue(String key) throws StorageBackendException {
   418 		//log.log(Level.SEVERE, "TODO: getConfigValue {0}", new Object[]{key});
   419 		return null;
   420 	}
   421 
   422 	@Override
   423 	public void setConfigValue(String key, String value) throws StorageBackendException {
   424 		log.log(Level.SEVERE, "TODO: setConfigValue {0} = {1}", new Object[]{key, value});
   425 	}
   426 
   427 	@Override
   428 	public int getEventsCount(int eventType, long startTimestamp, long endTimestamp, Group group) throws StorageBackendException {
   429 		log.log(Level.SEVERE, "TODO: getEventsCount {0} / {1} / {2} / {3}", new Object[]{eventType, startTimestamp, endTimestamp, group});
   430 		return 0;
   431 	}
   432 
   433 	@Override
   434 	public double getEventsPerHour(int key, long gid) throws StorageBackendException {
   435 		log.log(Level.SEVERE, "TODO: getEventsPerHour {0} / {1}", new Object[]{key, gid});
   436 		return 0;
   437 	}
   438 
   439 	@Override
   440 	public List<String> getGroupsForList(String listAddress) throws StorageBackendException {
   441 		log.log(Level.SEVERE, "TODO: getGroupsForList {0}", new Object[]{listAddress});
   442 		return Collections.emptyList();
   443 	}
   444 
   445 	@Override
   446 	public List<String> getListsForGroup(String groupname) throws StorageBackendException {
   447 		log.log(Level.SEVERE, "TODO: getListsForGroup {0}", new Object[]{groupname});
   448 		return Collections.emptyList();
   449 	}
   450 
   451 	@Override
   452 	public String getOldestArticle() throws StorageBackendException {
   453 		log.log(Level.SEVERE, "TODO: getOldestArticle");
   454 		return null;
   455 	}
   456 
   457 	@Override
   458 	public List<Subscription> getSubscriptions(int type) throws StorageBackendException {
   459 		log.log(Level.SEVERE, "TODO: getSubscriptions {0}", new Object[]{type});
   460 		return Collections.emptyList();
   461 	}
   462 
   463 	@Override
   464 	public void purgeGroup(Group group) throws StorageBackendException {
   465 		log.log(Level.SEVERE, "TODO: purgeGroup {0}", new Object[]{group});
   466 	}
   467 
   468 	@Override
   469 	public boolean update(Article article) throws StorageBackendException {
   470 		log.log(Level.SEVERE, "TODO: update {0}", new Object[]{article});
   471 		throw new StorageBackendException("Not implemented yet.");
   472 	}
   473 
   474 	@Override
   475 	public boolean update(Group group) throws StorageBackendException {
   476 		log.log(Level.SEVERE, "TODO: update {0}", new Object[]{group});
   477 		throw new StorageBackendException("Not implemented yet.");
   478 	}
   479 }