001: /**
002: * Copyright (C) 2001 Yasna.com. All rights reserved.
003: *
004: * ===================================================================
005: * The Apache Software License, Version 1.1
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The end-user documentation included with the redistribution,
020: * if any, must include the following acknowledgment:
021: * "This product includes software developed by
022: * Yasna.com (http://www.yasna.com)."
023: * Alternately, this acknowledgment may appear in the software itself,
024: * if and wherever such third-party acknowledgments normally appear.
025: *
026: * 4. The names "Yazd" and "Yasna.com" must not be used to
027: * endorse or promote products derived from this software without
028: * prior written permission. For written permission, please
029: * contact yazd@yasna.com.
030: *
031: * 5. Products derived from this software may not be called "Yazd",
032: * nor may "Yazd" appear in their name, without prior written
033: * permission of Yasna.com.
034: *
035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL YASNA.COM OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: *
049: * This software consists of voluntary contributions made by many
050: * individuals on behalf of Yasna.com. For more information
051: * on Yasna.com, please see <http://www.yasna.com>.
052: */
053:
054: /**
055: * Copyright (C) 2000 CoolServlets.com. All rights reserved.
056: *
057: * ===================================================================
058: * The Apache Software License, Version 1.1
059: *
060: * Redistribution and use in source and binary forms, with or without
061: * modification, are permitted provided that the following conditions
062: * are met:
063: *
064: * 1. Redistributions of source code must retain the above copyright
065: * notice, this list of conditions and the following disclaimer.
066: *
067: * 2. Redistributions in binary form must reproduce the above copyright
068: * notice, this list of conditions and the following disclaimer in
069: * the documentation and/or other materials provided with the
070: * distribution.
071: *
072: * 3. The end-user documentation included with the redistribution,
073: * if any, must include the following acknowledgment:
074: * "This product includes software developed by
075: * CoolServlets.com (http://www.coolservlets.com)."
076: * Alternately, this acknowledgment may appear in the software itself,
077: * if and wherever such third-party acknowledgments normally appear.
078: *
079: * 4. The names "Jive" and "CoolServlets.com" must not be used to
080: * endorse or promote products derived from this software without
081: * prior written permission. For written permission, please
082: * contact webmaster@coolservlets.com.
083: *
084: * 5. Products derived from this software may not be called "Jive",
085: * nor may "Jive" appear in their name, without prior written
086: * permission of CoolServlets.com.
087: *
088: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
089: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
090: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
091: * DISCLAIMED. IN NO EVENT SHALL COOLSERVLETS.COM OR
092: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
093: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
094: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
095: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
096: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
097: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
098: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
099: * SUCH DAMAGE.
100: * ====================================================================
101: *
102: * This software consists of voluntary contributions made by many
103: * individuals on behalf of CoolServlets.com. For more information
104: * on CoolServlets.com, please see <http://www.coolservlets.com>.
105: */package com.Yasna.forum.database;
106:
107: import java.sql.*;
108: import java.util.Date;
109: import java.util.Iterator;
110: import java.util.Calendar;
111:
112: import com.Yasna.forum.*;
113: import com.Yasna.util.*;
114: import com.Yasna.util.Cacheable;
115: import com.Yasna.util.CacheSizes;
116:
117: /**
118: * Database implementation of the ForumThread interface.
119: *
120: * @see ForumThread
121: */
122: public class DbForumThread implements ForumThread, Cacheable {
123:
124: /** DATABASE QUERIES **/
125: private static final String MESSAGE_COUNT = "SELECT count(*) FROM yazdMessage WHERE threadID=?";
126: private static final String ADD_MESSAGE = "INSERT INTO yazdMessageTree(parentID,childID) VALUES(?,?)";
127: private static final String MOVE_MESSAGE = "UPDATE yazdMessageTree SET parentID=? WHERE childID=?";
128: private static final String CHANGE_MESSAGE_THREAD = "UPDATE yazdMessage SET threadID=? WHERE messageID=?";
129: private static final String UPDATE_THREAD_MODIFIED_DATE = "UPDATE yazdThread SET modifiedDate=? WHERE threadID=?";
130: private static final String DELETE_MESSAGE1 = "DELETE FROM yazdMessageTree WHERE childID=?";
131: private static final String DELETE_MESSAGE2 = "DELETE FROM yazdMessage WHERE messageID=?";
132: private static final String DELETE_MESSAGE_PROPERTIES = "DELETE FROM yazdMessageProp WHERE messageID=?";
133: private static final String LOAD_THREAD = "SELECT rootMessageID, creationDate, modifiedDate, approved, readcnt,typeID,sticky,closedflag FROM yazdThread WHERE threadID=? and forumID=?";
134: private static final String INSERT_THREAD = "INSERT INTO yazdThread(threadID,forumID, rootMessageID,creationDate,"
135: + "modifiedDate,approved,readcnt,typeID,sticky,closedflag) VALUES(?,?,?,?,?,?,0,?,0,0)";
136: private static final String UPDATE_READ_CNT = "update yazdThread set readcnt=readcnt+1 where threadID=?";
137: private static final String SAVE_THREAD = "UPDATE yazdThread SET rootMessageID=?, creationDate=?, "
138: + "modifiedDate=?, approved = ?,sticky=?,closedflag=? WHERE threadID=?";
139:
140: private int id = -1;
141: //Temporary object reference for when inserting new record.
142: private ForumMessage rootMessage;
143: private int rootMessageID;
144: private int readcount;
145: private java.util.Date creationDate;
146: private java.util.Date modifiedDate;
147: private boolean approved;
148: private boolean sticky = false;
149: private boolean closed = false;
150: private int typeID;
151: private long timestamp = Calendar.getInstance().getTimeInMillis();
152:
153: /**
154: * Indicates if the object is ready to be saved or not. An object is not
155: * ready to be saved if it has just been created and has not yet been added
156: * to its container. For example, a message added to a thread, etc.
157: */
158: private boolean isReadyToSave = false;
159:
160: /**
161: * The forum allows us access to the message filters.
162: */
163: private DbForum forum;
164:
165: /**
166: * The factory provides services such as db connections and logging.
167: */
168: private DbForumFactory factory;
169:
170: /**
171: * Creates a new DbForumThread. The supplied message object is used to
172: * derive the name of the thread (subject of message), as well as the
173: * creation date and modified date of thread.
174: *
175: * @param rootMessage the root message of the thread.
176: */
177: protected DbForumThread(ForumMessage rootMessage, boolean approved,
178: DbForum forum, DbForumFactory factory, ThreadType type)
179: throws UnauthorizedException {
180: this .id = DbSequenceManager.nextID("ForumThread");
181: this .forum = forum;
182: this .factory = factory;
183: this .rootMessage = rootMessage;
184: this .rootMessageID = rootMessage.getID();
185: //Set the creation and modified dates to be the same as those of
186: //root message.
187: long rootMessageTime = rootMessage.getCreationDate().getTime();
188: this .creationDate = new java.util.Date(rootMessageTime);
189: this .modifiedDate = new java.util.Date(rootMessageTime);
190: this .approved = approved;
191: this .typeID = type.getID();
192: }
193:
194: /**
195: * Loads a DbForumThread from the database based on its id.
196: *
197: * @param id in unique id of the ForumThread to load.
198: * @param forum the Forum that the thread belongs to.
199: * @param factory a ForumFactory to use for loading.
200: */
201: protected DbForumThread(int id, DbForum forum,
202: DbForumFactory factory) throws ForumThreadNotFoundException {
203: this .id = id;
204: this .forum = forum;
205: this .factory = factory;
206: loadFromDb();
207: isReadyToSave = true;
208: }
209:
210: //FROM THE FORUMMESSAGE INTERFACE//
211:
212: public int getID() {
213: return id;
214: }
215:
216: public String getName() {
217: return getRootMessage().getSubject();
218: }
219:
220: public boolean isApproved() {
221: return approved;
222: }
223:
224: public java.util.Date getCreationDate() {
225: return creationDate;
226: }
227:
228: public void setCreationDate(java.util.Date creationDate)
229: throws UnauthorizedException {
230: this .creationDate = creationDate;
231: //Only save to the db if the object is ready
232: if (!isReadyToSave) {
233: return;
234: }
235: saveToDb();
236: }
237:
238: public java.util.Date getModifiedDate() {
239: return modifiedDate;
240: }
241:
242: public void setModifiedDate(java.util.Date modifiedDate)
243: throws UnauthorizedException {
244: this .modifiedDate = modifiedDate;
245: //Only save to the db if the object is ready
246: if (!isReadyToSave) {
247: return;
248: }
249: saveToDb();
250: }
251:
252: public void setApprovment(boolean approved)
253: throws UnauthorizedException {
254: this .approved = approved;
255: //Only save to the db if the object is ready
256: if (!isReadyToSave) {
257: return;
258: }
259: saveToDb();
260: this .getRootMessage().setApprovment(approved);
261: }
262:
263: public Forum getForum() {
264: return forum;
265: }
266:
267: public ForumMessage getMessage(int messageID)
268: throws ForumMessageNotFoundException {
269: ForumMessage message = factory.getMessage(messageID);
270:
271: //Apply filters to message.
272: message = forum.applyFilters(message);
273: return message;
274: }
275:
276: public ForumMessage getRootMessage() {
277: try {
278: return getMessage(rootMessageID);
279: } catch (ForumMessageNotFoundException e) {
280: System.err.println("Could not load root message with id "
281: + rootMessageID);
282: e.printStackTrace();
283: return null;
284: }
285: /*DbForumMessage message = (DbForumMessage)factory.cacheManager.get(
286: DbCacheManager.MESSAGE_CACHE,
287: new Integer(rootMessageID)
288: );
289: if (message == null) {
290: //Load and add to cache
291: try {
292: message = new DbForumMessage(rootMessageID, factory);
293: factory.cacheManager.add(DbCacheManager.MESSAGE_CACHE, new Integer(rootMessageID), message);
294: }
295: catch (ForumMessageNotFoundException e) {
296: System.err.println("Could not load root message with id " + rootMessageID);
297: e.printStackTrace();
298: }
299: }
300: return message;
301: */
302: }
303:
304: public int getMessageCount() {
305: int messageCount = 0;
306: Connection con = null;
307: PreparedStatement pstmt = null;
308: try {
309: con = DbConnectionManager.getConnection();
310: pstmt = con.prepareStatement(MESSAGE_COUNT);
311: pstmt.setInt(1, id);
312: ResultSet rs = pstmt.executeQuery();
313: rs.next();
314: messageCount = rs.getInt(1);
315: } catch (SQLException sqle) {
316: System.err
317: .println("DbForumThread:getMessageCount() failed: "
318: + sqle);
319: } finally {
320: try {
321: pstmt.close();
322: } catch (Exception e) {
323: e.printStackTrace();
324: }
325: try {
326: con.close();
327: } catch (Exception e) {
328: e.printStackTrace();
329: }
330: }
331: return messageCount;
332: }
333:
334: public int getReadCount() {
335: return this .readcount;
336: }
337:
338: public void addReadCount() {
339: Connection con = null;
340: PreparedStatement pstmt = null;
341: try {
342: this .readcount++;
343: con = DbConnectionManager.getConnection();
344: pstmt = con.prepareStatement(UPDATE_READ_CNT);
345: pstmt.setInt(1, id);
346: pstmt.executeUpdate();
347: //System.err.println(this.timestamp +" id "+ this.id+" - count:"+readcount);
348: } catch (SQLException sqle) {
349: System.err.println("DbForumThread:addReadCount() failed: "
350: + sqle);
351: } finally {
352: try {
353: pstmt.close();
354: } catch (Exception e) {
355: e.printStackTrace();
356: }
357: try {
358: con.close();
359: } catch (Exception e) {
360: e.printStackTrace();
361: }
362: }
363:
364: }
365:
366: public void addMessage(ForumMessage parentMessage,
367: ForumMessage newMessage) throws UnauthorizedException {
368: boolean abortTransaction = false;
369: boolean supportsTransactions = false;
370: //Add message to db
371: Connection con = null;
372: PreparedStatement pstmt = null;
373: try {
374: con = DbConnectionManager.getConnection();
375: supportsTransactions = con.getMetaData()
376: .supportsTransactions();
377: if (supportsTransactions) {
378: con.setAutoCommit(false);
379: }
380:
381: //Now, insert the message into the database.
382: ((ForumMessageProxy) newMessage).insertIntoDb(con, this );
383:
384: pstmt = con.prepareStatement(ADD_MESSAGE);
385: pstmt.setInt(1, parentMessage.getID());
386: pstmt.setInt(2, newMessage.getID());
387: pstmt.executeUpdate();
388: pstmt.close();
389:
390: //if the user wants to automatically subscribe to the thread then we add it.
391: User newUser = newMessage.getUser();
392: if (newUser.getThreadSubscribe() && newUser.getID() > 1) {
393: newUser.setProperty("WatchThread" + id, "true");
394: }
395:
396: } catch (Exception e) {
397: e.printStackTrace();
398: abortTransaction = true;
399: return;
400: } finally {
401: try {
402: if (supportsTransactions) {
403: if (abortTransaction == true) {
404: con.rollback();
405: } else {
406: con.commit();
407: }
408: }
409: } catch (Exception e) {
410: e.printStackTrace();
411: }
412: try {
413: if (supportsTransactions) {
414: con.setAutoCommit(true);
415: }
416: con.close();
417: } catch (Exception e) {
418: e.printStackTrace();
419: }
420: }
421:
422: //Added new message, so update the modified date of this thread
423: updateModifiedDate(newMessage.getModifiedDate());
424: //Also, update the modified date of the forum
425: DbForum dbForum = (DbForum) factory.cacheManager.get(
426: DbCacheManager.FORUM_CACHE, new Integer(forum.getID()));
427: if (dbForum != null) {
428: dbForum.updateModifiedDate(modifiedDate);
429: } else {
430: forum.updateModifiedDate(modifiedDate);
431: }
432: }
433:
434: public void deleteMessage(ForumMessage message)
435: throws UnauthorizedException {
436: //Skip null messages or the case that we're already deleting the thread.
437: if (message == null) {
438: return;
439: }
440: //If the message does not belong to this thread, don't perform delete.
441: if (message.getForumThread().getID() != this .id) {
442: throw new IllegalArgumentException("Message "
443: + message.getID()
444: + " could not be deleted. It belongs to thread "
445: + message.getForumThread().getID()
446: + ", and not thread " + this .id + ".");
447: }
448: Connection con = null;
449: PreparedStatement pstmt = null;
450: try {
451: con = DbConnectionManager.getConnection();
452: //Delete the message from the parent/child table
453: pstmt = con.prepareStatement(DELETE_MESSAGE1);
454: pstmt.setInt(1, message.getID());
455: pstmt.execute();
456: } catch (SQLException sqle) {
457: System.err
458: .println("Error in DbForumThread:deleteMessage()-"
459: + sqle);
460: } finally {
461: try {
462: pstmt.close();
463: } catch (Exception e) {
464: e.printStackTrace();
465: }
466: try {
467: con.close();
468: } catch (Exception e) {
469: e.printStackTrace();
470: }
471: }
472:
473: //Recursively delete all children
474: TreeWalker walker = treeWalker();
475: int childCount = walker.getChildCount(message);
476: for (int i = childCount - 1; i >= 0; i--) {
477: ForumMessage childMessage = walker.getChild(message, i);
478: if (childMessage == null) {
479: System.err.println("child message was null -- index "
480: + i);
481: }
482: deleteMessage(childMessage);
483: }
484: try {
485: //Delete the actual message.
486: con = DbConnectionManager.getConnection();
487: pstmt = con.prepareStatement(DELETE_MESSAGE2);
488: pstmt.setInt(1, message.getID());
489: pstmt.execute();
490: pstmt.close();
491:
492: //Delete any message properties.
493: pstmt = con.prepareStatement(DELETE_MESSAGE_PROPERTIES);
494: pstmt.setInt(1, message.getID());
495: pstmt.execute();
496: } catch (SQLException sqle) {
497: System.err
498: .println("Error in DbForumThread:deleteMessage()-"
499: + sqle);
500: } finally {
501: try {
502: pstmt.close();
503: } catch (Exception e) {
504: e.printStackTrace();
505: }
506: try {
507: con.close();
508: } catch (Exception e) {
509: e.printStackTrace();
510: }
511: }
512: //Now, delete from the cache.
513: factory.getCacheManager().remove(DbCacheManager.MESSAGE_CACHE,
514: new Integer(message.getID()));
515:
516: //Finally, delete it from the search index
517: factory.getSearchIndexer().removeFromIndex(message);
518:
519: //Now, make sure that the message being deleted isn't the root message
520: //of this thread. If it is, the whole thread should just be deleted.
521: if (message.getID() == this .rootMessageID) {
522: forum.deleteThreadRecord(this .id);
523: }
524: }
525:
526: public void moveMessage(ForumMessage message,
527: ForumThread newThread, ForumMessage parentMessage)
528: throws UnauthorizedException, IllegalArgumentException {
529: if (message.getForumThread().getID() != this .id
530: || parentMessage.getForumThread().getID() != newThread
531: .getID()) {
532: throw new IllegalArgumentException(
533: "The messages and threads did not match.");
534: }
535:
536: // Save the mesageID of message to move
537: int messageID = message.getID();
538: // Original message thread rootMessageID
539: int oldRootMessageID = getRootMessage().getID();
540:
541: // Move the children of this message to the new thread
542: TreeWalker walker = treeWalker();
543: int childCount = walker.getChildCount(message);
544: for (int i = 0; i < childCount; i++) {
545: ForumMessage childMessage = walker.getChild(message, i);
546: changeMessageThread(childMessage, newThread);
547: }
548:
549: //Move the message to the new thread.
550: changeMessageThread(message, newThread);
551:
552: //Make message a child of parentMessage
553: Connection con = null;
554: PreparedStatement pstmt = null;
555: try {
556: con = DbConnectionManager.getConnection();
557:
558: if (oldRootMessageID != messageID) {
559: pstmt = con.prepareStatement(MOVE_MESSAGE);
560: pstmt.setInt(1, parentMessage.getID());
561: pstmt.setInt(2, messageID);
562: } else {
563: pstmt = con.prepareStatement(ADD_MESSAGE);
564: pstmt.setInt(1, parentMessage.getID());
565: pstmt.setInt(2, messageID);
566: }
567:
568: pstmt.executeUpdate();
569: pstmt.close();
570: } catch (SQLException sqle) {
571: System.err.println("Error in DbForumThread:moveMessage()-"
572: + sqle);
573: } finally {
574: try {
575: pstmt.close();
576: } catch (Exception e) {
577: e.printStackTrace();
578: }
579: try {
580: con.close();
581: } catch (Exception e) {
582: e.printStackTrace();
583: }
584: }
585:
586: //Update the modified date of newThread
587: Date now = new Date();
588: newThread.setModifiedDate(now);
589: //Update the modified date of newThread forum
590: newThread.getForum().setModifiedDate(now);
591:
592: //Thread has been modified, invalidate the cache
593: DbCacheManager cacheManager = factory.getCacheManager();
594: Integer key = new Integer(this .id);
595: cacheManager.remove(DbCacheManager.THREAD_CACHE, key);
596:
597: //If we moved the root message of this thread, the thread should be
598: //deleted. Normally, deleting a thread will delete all of it's messages.
599: //However, we've already adjusted the thread/message relationship at the
600: //SQL level and removed the thread from cache. That should mean we're safe.
601: if (getRootMessage().getID() == messageID) {
602: //rootMessage = null;
603: this .getForum().deleteThread(this );
604: }
605: }
606:
607: public TreeWalker treeWalker() {
608: return new DbTreeWalker(this , factory);
609: }
610:
611: public Iterator messages() {
612: return new DbThreadIterator(this );
613: }
614:
615: public Iterator messages(int startIndex, int numResults) {
616: return new DbThreadIterator(this , startIndex, numResults);
617: }
618:
619: public boolean hasPermission(int type) {
620: return true;
621: }
622:
623: //FROM THE CACHEABLE INTERFACE//
624:
625: public int getSize() {
626: //Approximate the size of the object in bytes by calculating the size
627: //of each field.
628: int size = 0;
629: size += CacheSizes.sizeOfObject(); //overhead of object
630: size += CacheSizes.sizeOfInt(); //id
631: size += CacheSizes.sizeOfDate(); //creation date
632: size += CacheSizes.sizeOfDate(); //modified date
633: size += CacheSizes.sizeOfBoolean(); //approved
634: size += CacheSizes.sizeOfObject(); //ref to rootMessage
635: size += CacheSizes.sizeOfObject(); //ref to forum
636: size += CacheSizes.sizeOfObject(); //ref to factory
637: size += CacheSizes.sizeOfBoolean(); //ready save var
638: size += CacheSizes.sizeOfBoolean(); //deleting var
639: size += CacheSizes.sizeOfInt(); //read count
640: size += CacheSizes.sizeOfLong(); //timestamp
641:
642: return size;
643: }
644:
645: //OTHER METHODS//
646:
647: /**
648: * Converts the object to a String by returning the name of the thread.
649: * This functionality is primarily for Java applications that might be
650: * accessing Yazd objects through a GUI.
651: */
652: public String toString() {
653: return getName();
654: }
655:
656: public int hashCode() {
657: return id;
658: }
659:
660: public boolean equals(Object object) {
661: if (this == object) {
662: return true;
663: }
664: if (object != null && object instanceof DbForumThread) {
665: return id == ((DbForumThread) object).getID();
666: } else {
667: return false;
668: }
669: }
670:
671: /**
672: * Updates the modified date but doesn't require a security check since
673: * it is a protected method.
674: */
675: protected void updateModifiedDate(java.util.Date modifiedDate) {
676: this .modifiedDate = modifiedDate;
677: Connection con = null;
678: PreparedStatement pstmt = null;
679: try {
680: con = DbConnectionManager.getConnection();
681: pstmt = con.prepareStatement(UPDATE_THREAD_MODIFIED_DATE);
682: pstmt.setString(1, "" + modifiedDate.getTime());
683: pstmt.setInt(2, id);
684: pstmt.executeUpdate();
685: } catch (SQLException sqle) {
686: System.err
687: .println("Error in DbForumThread:updateModifiedDate()-"
688: + sqle);
689: sqle.printStackTrace();
690: } finally {
691: try {
692: pstmt.close();
693: } catch (Exception e) {
694: e.printStackTrace();
695: }
696: try {
697: con.close();
698: } catch (Exception e) {
699: e.printStackTrace();
700: }
701: }
702: }
703:
704: /**
705: * Moves a message to a new thread by modifying the message table threadID
706: * column.
707: *
708: * @param message the message to move.
709: * @param newThread the thread to move the message to.
710: */
711: private void changeMessageThread(ForumMessage message,
712: ForumThread newThread) throws UnauthorizedException {
713: //Remove message from the search index
714: factory.getSearchIndexer().removeFromIndex(message);
715:
716: //Remove message from cache.
717: DbCacheManager cacheManager = factory.getCacheManager();
718: Integer key = new Integer(message.getID());
719: cacheManager.remove(DbCacheManager.MESSAGE_CACHE, key);
720:
721: Connection con = null;
722: PreparedStatement pstmt = null;
723: try {
724: con = DbConnectionManager.getConnection();
725: pstmt = con.prepareStatement(CHANGE_MESSAGE_THREAD);
726: pstmt.setInt(1, newThread.getID());
727: pstmt.setInt(2, key.intValue());
728: pstmt.executeUpdate();
729: } catch (SQLException sqle) {
730: sqle.printStackTrace();
731: }
732:
733: // Add message back to search index and update modified date
734: try {
735: ForumMessage movedMessage = newThread.getMessage(key
736: .intValue());
737: factory.getSearchIndexer().addToIndex(movedMessage);
738: movedMessage.setModifiedDate(new Date());
739: } catch (ForumMessageNotFoundException e) {
740: System.err
741: .println("Error in DbForumThread:changeMessageThread()-"
742: + "messageID="
743: + key.intValue()
744: + "newThreadID=" + newThread.getID());
745: }
746:
747: finally {
748: try {
749: pstmt.close();
750: } catch (Exception e) {
751: e.printStackTrace();
752: }
753: try {
754: con.close();
755: } catch (Exception e) {
756: e.printStackTrace();
757: }
758: }
759: }
760:
761: /**
762: * Loads a ForumThread from the database.
763: */
764: private void loadFromDb() throws ForumThreadNotFoundException {
765: Connection con = null;
766: PreparedStatement pstmt = null;
767: try {
768: con = DbConnectionManager.getConnection();
769: pstmt = con.prepareStatement(LOAD_THREAD);
770: pstmt.setInt(1, id);
771: pstmt.setInt(2, forum.getID());
772: ResultSet rs = pstmt.executeQuery();
773: if (!rs.next()) {
774: throw new ForumThreadNotFoundException("Thread " + id
775: + " could not be loaded from the database.");
776: }
777: //try {
778: rootMessageID = rs.getInt("rootMessageID");
779: //}
780: //catch (ForumMessageNotFoundException fmnfe) {
781: // System.err.println("Error: could not load root message of thread "
782: // + id + ". The database record could be corrupt.");
783: // fmnfe.printStackTrace();
784: //}
785: creationDate = new java.util.Date(Long.parseLong(rs
786: .getString("creationDate").trim()));
787: modifiedDate = new java.util.Date(Long.parseLong(rs
788: .getString("modifiedDate").trim()));
789: approved = rs.getInt("approved") == 1 ? true : false;
790: readcount = rs.getInt("readcnt");
791: typeID = rs.getInt("typeID");
792: closed = rs.getInt("closedflag") == 1 ? true : false;
793: sticky = rs.getInt("sticky") == 1 ? true : false;
794: pstmt.close();
795: } catch (SQLException sqle) {
796: throw new ForumThreadNotFoundException("Thread " + id
797: + " could not be loaded from the database.");
798: } catch (NumberFormatException nfe) {
799: System.err
800: .println("WARNING: In DbForumThread.loadFromDb() -- there "
801: + "was an error parsing the dates returned from the database. Ensure "
802: + "that they're being stored correctly.");
803: } finally {
804: try {
805: con.close();
806: } catch (Exception e) {
807: e.printStackTrace();
808: }
809: }
810: }
811:
812: /**
813: * Inserts a new forum thread into the database. A connection object must
814: * be passed in. The connection must be open when passed in, and will
815: * remain open when passed back. This method allows us to make insertions
816: * be transactional.
817: *
818: * @param con an open Connection used to insert the thread to the db.
819: */
820: public void insertIntoDb(Connection con) throws SQLException {
821: PreparedStatement pstmt = con.prepareStatement(INSERT_THREAD);
822: pstmt.setInt(1, id);
823: pstmt.setInt(2, forum.getID());
824: pstmt.setInt(3, rootMessageID);
825: pstmt.setString(4, Long.toString(creationDate.getTime()));
826: pstmt.setString(5, Long.toString(modifiedDate.getTime()));
827: pstmt.setInt(6, approved ? 1 : 0);
828: pstmt.setInt(7, typeID);
829: pstmt.executeUpdate();
830: pstmt.close();
831:
832: //Now, insert the message into the database.
833: ((ForumMessageProxy) rootMessage).insertIntoDb(con, this );
834:
835: //since we're done inserting the object to the database, it is ready
836: //for future insertions.
837: isReadyToSave = true;
838: }
839:
840: /**
841: * Saves the ForumThread to the database.
842: */
843: private synchronized void saveToDb() {
844: Connection con = null;
845: PreparedStatement pstmt = null;
846: try {
847: con = DbConnectionManager.getConnection();
848: pstmt = con.prepareStatement(SAVE_THREAD);
849: pstmt.setInt(1, rootMessageID);
850: pstmt.setString(2, Long.toString(creationDate.getTime()));
851: pstmt.setString(3, Long.toString(modifiedDate.getTime()));
852: pstmt.setInt(4, approved ? 1 : 0);
853: pstmt.setInt(5, sticky ? 1 : 0);
854: pstmt.setInt(6, closed ? 1 : 0);
855: pstmt.setInt(7, id);
856: pstmt.executeUpdate();
857: } catch (SQLException sqle) {
858: System.err.println("Error in DbForumThread:saveToDb()-"
859: + sqle);
860: } finally {
861: try {
862: pstmt.close();
863: } catch (Exception e) {
864: e.printStackTrace();
865: }
866: try {
867: con.close();
868: } catch (Exception e) {
869: e.printStackTrace();
870: }
871: }
872: }
873:
874: public ThreadType getThreadType() {
875: return factory.getThreadType(this .typeID);
876: }
877:
878: public boolean isSticky() {
879: return this .sticky;
880: }
881:
882: public void setSticky(boolean param) throws UnauthorizedException {
883: this .sticky = param;
884: saveToDb();
885: }
886:
887: public boolean isClosed() {
888: return this .closed;
889: }
890:
891: public void setClosed(boolean param) throws UnauthorizedException {
892: this.closed = param;
893: saveToDb();
894: }
895:
896: }
|