/** * $RCSfile: DbForum.java,v $ * $Revision: 1.10.2.6 $ * $Date: 2001/04/06 05:02:27 $ * * Copyright (C) 2000 CoolServlets.com. All rights reserved. * * =================================================================== * The Apache Software License, Version 1.1 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by * CoolServlets.com (http://www.coolservlets.com)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Jive" and "CoolServlets.com" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please * contact webmaster@coolservlets.com. * * 5. Products derived from this software may not be called "Jive", * nor may "Jive" appear in their name, without prior written * permission of CoolServlets.com. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL COOLSERVLETS.COM OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of CoolServlets.com. For more information * on CoolServlets.com, please see . */ package com.coolservlets.forum.database; import java.util.Iterator; import java.util.Enumeration; import java.util.Properties; import java.util.ArrayList; import java.util.Date; import java.sql.*; import java.io.*; import com.coolservlets.forum.*; import com.coolservlets.forum.filter.*; import com.coolservlets.util.Cache; import com.coolservlets.util.Cacheable; import com.coolservlets.util.CacheSizes; /** * Database implementation of the Forum interface. It loads and stores forum * information from a a database. * * @see Forum */ public class DbForum implements Forum, Cacheable { /** DATABASE QUERIES **/ private static final String ADD_THREAD = "UPDATE jiveThread set forumID=? WHERE threadID=?"; protected static final String DELETE_THREAD = "DELETE FROM jiveThread WHERE threadID=?"; private static final String THREAD_COUNT = "SELECT count(*) FROM jiveThread WHERE forumID=?"; private static final String MESSAGE_COUNT = "SELECT count(*) FROM jiveThread, jiveMessage WHERE " + "jiveThread.forumID=? AND jiveThread.threadID=jiveMessage.threadID"; private static final String ADD_USER_PERM = "INSERT INTO jiveUserPerm(forumID,userID,permission) VALUES(?,?,?)"; private static final String REMOVE_USER_PERM = "DELETE FROM jiveUserPerm WHERE forumID=? AND userID=? AND permission=?"; private static final String USERS_WITH_PERM = "SELECT DISTINCT userID FROM jiveUserPerm WHERE forumID=? AND permission=?"; private static final String ADD_GROUP_PERM = "INSERT INTO jiveGroupPerm(forumID,groupID,permission) VALUES(?,?,?)"; private static final String REMOVE_GROUP_PERM = "DELETE FROM jiveGroupPerm WHERE forumID=? AND groupID=? AND permission=?"; private static final String GROUPS_WITH_PERM = "SELECT DISTINCT groupID FROM jiveGroupPerm WHERE forumID=? AND permission=?"; private static final String LOAD_FILTERS = "SELECT filterObject, filterIndex FROM jiveFilter WHERE forumID=? ORDER BY filterIndex ASC"; private static final String DELETE_FILTERS = "DELETE FROM jiveFilter WHERE forumID=?"; private static final String ADD_FILTER = "INSERT INTO jiveFilter(forumID,filterIndex,filterObject) VALUES(?,?,?)"; private static final String LOAD_PROPERTIES = "SELECT name, propValue FROM jiveForumProp WHERE forumID=?"; private static final String DELETE_PROPERTIES = "DELETE FROM jiveForumProp WHERE forumID=?"; private static final String INSERT_PROPERTY = "INSERT INTO jiveForumProp(forumID,name,propValue) VALUES(?,?,?)"; private static final String LOAD_FORUM_BY_ID = "SELECT forumID, name, description, creationDate, modifiedDate, moderated FROM jiveForum WHERE forumID=?"; private static final String LOAD_FORUM_BY_NAME = "SELECT forumID, name, description, creationDate, modifiedDate, moderated FROM jiveForum WHERE name=?"; private static final String ADD_FORUM = "INSERT INTO jiveForum(forumID, name, description, creationDate, " + "modifiedDate, moderated) VALUES (?,?,?,?,?,?)"; private static final String SAVE_FORUM = "UPDATE jiveForum SET name=?, description=?, creationDate=?, " + "modifiedDate=?, moderated=? WHERE forumID=?"; private static final String UPDATE_FORUM_MODIFIED_DATE = "UPDATE jiveForum SET modifiedDate=? WHERE forumID=?"; private int id = -1; private String name; private String description; private java.util.Date creationDate; private java.util.Date modifiedDate; private int moderated = 0; private ForumMessageFilter[] filters; private Properties properties; //Lock for saving state to database. private Object saveLock = new Object(); private DbForumFactory factory; /** * Creates a new forum with the specified name and description. * * @param name the name of the forum. * @param description the description of the forum. * @param factory the DbForumFactory the forum is a part of. */ protected DbForum(String name, String description, DbForumFactory factory) { this.id = DbSequenceManager.nextID("Forum"); this.name = name; this.description = description; long now = System.currentTimeMillis(); creationDate = new java.util.Date(now); modifiedDate = new java.util.Date(now); this.factory = factory; insertIntoDb(); properties = new Properties(); //Forums should start with an html filter by default for //security purposes. filters = new ForumMessageFilter[0]; //try { // addForumMessageFilter(new FilterHtml(), 0); // addForumMessageFilter(new FilterNewline(), 1); //} //catch (UnauthorizedException ue) { // ue.printStackTrace(); //} } /** * Loads a forum with the specified id. */ protected DbForum(int id, DbForumFactory factory) throws ForumNotFoundException { this.id = id; this.factory = factory; loadFromDb(); loadFiltersFromDb(); loadProperties(); } /** * Loads a forum with the specified name. */ protected DbForum(String name, DbForumFactory factory) throws ForumNotFoundException { this.name = name; this.factory = factory; loadFromDb(); loadFiltersFromDb(); loadProperties(); } //FROM THE FORUM INTERFACE// public int getID() { return id; } public String getName() { return name; } public void setName(String name) throws UnauthorizedException { this.name = name; saveToDb(); } public String getDescription() { return description; } public void setDescription(String description) throws UnauthorizedException { this.description = description; saveToDb(); } public java.util.Date getCreationDate() { return creationDate; } public void setCreationDate(java.util.Date creationDate) throws UnauthorizedException { this.creationDate = creationDate; saveToDb(); } public java.util.Date getModifiedDate() { return modifiedDate; } public void setModifiedDate(java.util.Date modifiedDate) throws UnauthorizedException { this.modifiedDate = modifiedDate; saveToDb(); } public String getProperty(String name) { return (String)properties.get(name); } public void setProperty(String name, String value) throws UnauthorizedException { properties.put(name, value); saveProperties(); } public Enumeration propertyNames() { return properties.keys(); } public boolean isModerated(int type) { return false; //if (type == Forum.THREAD) { // return threadsModerated; //} //else { // return messagesModerated; //} } public void setModerated(int type, boolean moderated) throws UnauthorizedException { //if (type == Forum.THREAD) { // threadsModerated = moderated; //} //else { // messagesModerated = moderated; //} } public ForumThread createThread(ForumMessage rootMessage) throws UnauthorizedException { //If the forum is moderated, the thread is not automatically //approved. boolean approved = true; return new DbForumThread(rootMessage, approved, this, factory); } public ForumMessage createMessage(User user) throws UnauthorizedException { //If the forum is moderated, the message is not automatically //approved. boolean approved = !isModerated(JiveConstants.MESSAGE); return new DbForumMessage(user, factory); } public void addThread(ForumThread thread) throws UnauthorizedException { boolean abortTransaction = false; boolean supportsTransactions = false; //Add message to db Connection con = null; PreparedStatement pstmt = null; try { con = DbConnectionManager.getConnection(); supportsTransactions = con.getMetaData().supportsTransactions(); if (supportsTransactions) { con.setAutoCommit(false); } pstmt = con.prepareStatement(ADD_THREAD); pstmt.setInt(1,id); pstmt.setInt(2,thread.getID()); pstmt.executeUpdate(); pstmt.close(); //Now, insert the thread into the database. ((ForumThreadProxy)thread).insertIntoDb(con); } catch(Exception e) { e.printStackTrace(); abortTransaction = true; return; } finally { try { if (supportsTransactions) { if (abortTransaction == true) { con.rollback(); } else { con.commit(); } } } catch (Exception e) { e.printStackTrace(); } try { if (supportsTransactions) { con.setAutoCommit(true); } con.close(); } catch (Exception e) { e.printStackTrace(); } } //Since we added a thread, update the modified date of this thread. updateModifiedDate(thread.getModifiedDate()); } public ForumThread getThread(int threadID) throws ForumThreadNotFoundException { return factory.getThread(threadID, this); } public void deleteThread(ForumThread thread) throws UnauthorizedException { //Delete all messages from the thread. Deleting the root //message will delete all submessages. ForumMessage message = thread.getRootMessage(); thread.deleteMessage(message); } protected void deleteThreadRecord(int threadID) { //Delete the actual thread Connection con = null; PreparedStatement pstmt = null; try { con = DbConnectionManager.getConnection(); pstmt = con.prepareStatement(DELETE_THREAD); pstmt.setInt(1, threadID); pstmt.execute(); } catch( Exception sqle ) { System.err.println("Error in DbForum:deleteThread()-" + sqle); } finally { try { pstmt.close(); } catch (Exception e) { e.printStackTrace(); } try { con.close(); } catch (Exception e) { e.printStackTrace(); } } //Now, delete from cache Integer threadIDInteger = new Integer(threadID); factory.getCacheManager().remove(DbCacheManager.THREAD_CACHE, threadIDInteger); } public void moveThread(ForumThread thread, Forum forum) throws UnauthorizedException { //Ensure that thread belongs to this forum if (thread.getForum().getID() != this.id) { throw new IllegalArgumentException("The thread does not belong to this forum."); } //Modify the SQL record. Only the thread table has information about //forumID, so we only need to modify that record. The message records //underneath the thread can be left alone. Connection con = null; PreparedStatement pstmt = null; try { con = DbConnectionManager.getConnection(); pstmt = con.prepareStatement(ADD_THREAD); pstmt.setInt(1,forum.getID()); pstmt.setInt(2,thread.getID()); pstmt.executeUpdate(); pstmt.close(); } catch( SQLException sqle ) { System.err.println("Error in DbForum:addThread()-" + sqle); return; } finally { try { pstmt.close(); } catch (Exception e) { e.printStackTrace(); } try { con.close(); } catch (Exception e) { e.printStackTrace(); } } DbCacheManager cacheManager = factory.getCacheManager(); SearchIndexer indexer = factory.getSearchIndexer(); //Remove both forums from cache. Integer key = new Integer(this.id); cacheManager.remove(DbCacheManager.FORUM_CACHE, key); key = new Integer(forum.getID()); cacheManager.remove(DbCacheManager.FORUM_CACHE, key); //Remove thread from cache. key = new Integer(thread.getID()); cacheManager.remove(DbCacheManager.THREAD_CACHE, key); //Loop through all messages in thread Iterator messages = thread.messages(); while (messages.hasNext()) { ForumMessage message = (ForumMessage)messages.next(); //Remove each message from cache. key = new Integer(message.getID()); cacheManager.remove(DbCacheManager.MESSAGE_CACHE, key); //Remove and re-add every message to the search index. indexer.removeFromIndex(message); indexer.addToIndex(message); } // Update the modified date of thread Date now = new Date(); thread.setModifiedDate(now); // Update the modified date of forum thread is now in forum.setModifiedDate(now); } public Iterator threads() { return new DbForumIterator(this, factory); } public Iterator threads(int startIndex, int numResults) { return new DbForumIterator(this, factory, startIndex, numResults); } public int getThreadCount() { int threadCount = 0; // Based on the id in the object, get the thread data from the database: Connection con = null; PreparedStatement pstmt = null; try { con = DbConnectionManager.getConnection(); pstmt = con.prepareStatement(THREAD_COUNT); pstmt.setInt(1, id); ResultSet rs = pstmt.executeQuery(); rs.next(); threadCount = rs.getInt(1 /*"threadCount"*/); } catch( SQLException sqle ) { System.err.println("DbForum:getThreadCount() failed: " + sqle); } finally { try { pstmt.close(); } catch (Exception e) { e.printStackTrace(); } try { con.close(); } catch (Exception e) { e.printStackTrace(); } } return threadCount; } public int getMessageCount() { int messageCount = 0; Connection con = null; PreparedStatement pstmt = null; try { con = DbConnectionManager.getConnection(); pstmt = con.prepareStatement(MESSAGE_COUNT); pstmt.setInt(1, id); ResultSet rs = pstmt.executeQuery(); rs.next(); messageCount = rs.getInt(1 /*"messageCount"*/); } catch( SQLException sqle ) { System.err.println("DbForum:getMessageCount() failed: " + sqle); } finally { try { pstmt.close(); } catch (Exception e) { e.printStackTrace(); } try { con.close(); } catch (Exception e) { e.printStackTrace(); } } return messageCount; } public Query createQuery() { return new DbQuery(this, factory); } public void addUserPermission(User user, int permissionType) throws UnauthorizedException { Connection con = null; PreparedStatement pstmt = null; try { con = DbConnectionManager.getConnection(); pstmt = con.prepareStatement(ADD_USER_PERM); pstmt.setInt(1,id); pstmt.setInt(2,user.getID()); pstmt.setInt(3,permissionType); pstmt.execute(); //Remove user permissions from cache since they've changed. factory.getCacheManager().removeUserPerm( new Integer(user.getID()), new Integer(id) ); } catch( SQLException sqle ) { System.err.println("Error in DbForum.java:" + sqle); sqle.printStackTrace(); } finally { try { pstmt.close(); } catch (Exception e) { e.printStackTrace(); } try { con.close(); } catch (Exception e) { e.printStackTrace(); } } } public void removeUserPermission(User user, int permissionType) throws UnauthorizedException { Connection con = null; PreparedStatement pstmt = null; try { con = DbConnectionManager.getConnection(); pstmt = con.prepareStatement(REMOVE_USER_PERM); pstmt.setInt(1,id); pstmt.setInt(2,user.getID()); pstmt.setInt(3,permissionType); pstmt.execute(); //Remove user permissions from cache since they've changed. factory.getCacheManager().removeUserPerm( new Integer(user.getID()), new Integer(id) ); } catch( SQLException sqle ) { System.err.println("Error in DbForum.java:" + sqle); sqle.printStackTrace(); } finally { try { pstmt.close(); } catch (Exception e) { e.printStackTrace(); } try { con.close(); } catch (Exception e) { e.printStackTrace(); } } } public int[] usersWithPermission(int permissionType) throws UnauthorizedException { int [] users = new int[0]; Connection con = null; PreparedStatement pstmt = null; try { con = DbConnectionManager.getConnection(); pstmt = con.prepareStatement(USERS_WITH_PERM); pstmt.setInt(1,id); pstmt.setInt(2,permissionType); ResultSet rs = pstmt.executeQuery(); ArrayList userList = new ArrayList(); while (rs.next()) { userList.add(new Integer(rs.getInt("userID"))); } users = new int[userList.size()]; for (int i=0; i