Package org.openqueue.oqtane

Source Code of org.openqueue.oqtane.OQTopic

/* oqtane  (OpenQueue server classes and daemon)
*
* Copyright (c) 2000 oqtane Development Team (See Credits file)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
*/

package org.openqueue.oqtane;

import java.awt.Toolkit;
import java.net.*;
import java.io.*;
import java.sql.*;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.Vector;

public class OQTopic {
  public static Vector OQTopics;
  public final static int iDefaultTopicDBaseRowsAtStartup = 20;
  public final static int iDefaultTopicMaxItemsInRAM = 500;

  protected int topicID;
  protected String topicName;
  protected int owner;
  protected String aboutURL;
  protected boolean useAboutURLforItems;
  protected int flags;
  protected int readFlags = 1; // 0=nobody,1=owner,2=admin,4=ACL,8=world
  protected Vector topicItems;
  protected boolean persistent = true;

  protected static Hashtable pathsHash = new Hashtable ();
 
  private OQServer oqServer;
  private int latestMessageID;

  public int getLatestMessageID() {
    return latestMessageID;
  }

  public synchronized int getFirstIDinItems() {
    /* For example, if we erased the first 10,000 messages,
     * and first one left in vector is 10001.
     */
    
    if (topicItems.size() > 0) {
      OQItem anItem = (OQItem) topicItems.elementAt (0);
      return anItem.getMessageID();
    } else {
      return 0;
    }
  }

  static {
    if (OQTopics == null) OQTopics = new Vector ();
  }

  static void loadTopics (OQServer aServer) {
    String aCmd = new String();
    String aTopicName = new String();
    String aOwner = new String();
    String anID = new String();
    String aFlag = new String();
    OQServer.report(OQServer.repDebug, "Loading topics via database.");
    OQDBAccess aDB = aServer.GetDB();
    synchronized(aDB) {
      try {
        String sql = OQDBAccess.getSQLCommand(OQDBAccess.sql_SELECTALLTOPICS);
        ResultSet rs = aDB.getStatement().executeQuery( sql );

        OQDBAccess dbForItems = new OQDBAccess();
        OQServer.report (OQServer.repNormal, "Done new dbForItems");
        if ((dbForItems != null) && ( dbForItems.getConnectedOK()) )   {

          OQServer.report (OQServer.repDebugMinor, "Starting while loop");
          while(rs.next()) {

            // For some strange reason, the JDBC-ODBC Bridge with
            // MSAccess only allow one read of a field value
            //
            long       rsTopicID   = rs.getLong(1)   ;
            String     rsTitle     = rs.getString(2) ;
            long       rsOwner     = rs.getLong(3)   ;
            long       rsLastMessageID  = rs.getLong(4);
            int        rsPersistent   = rs.getInt(5);
            int        rsReadFlags    = rs.getInt(6);

            OQServer.report(OQServer.repDebug,
               "TopicID="    +rsTopicID       + "   " +
               "Title="      +rsTitle         + "   " +
               "Owner="      +rsOwner         + "   " +
               "LastMsgID="  +rsLastMessageID + "   " +
               "Persistent=" +rsPersistent     + "   " +
               "ReadFlags =" +rsReadFlags    );

            int pers         = rsPersistent;
            int tmpReadFlags = rsReadFlags;
            OQServer.report(OQServer.repDebug, "---");

            OQTopic bTopic = new OQTopic ( aServer,
                   (int) rsTopicID, rsTitle, (int) rsOwner, (int) rsLastMessageID, (int) rsPersistent, (int) rsReadFlags);
            if (pers == 1) {    /* should make it a bitwise test? */
                  bTopic.persistent = true;
                  OQServer.report(OQServer.repDebug, "-PERSISTENT-");
            } else {
                  bTopic.persistent = false;
                  OQServer.report(OQServer.repDebug, "-Not Persistent-");
            }

            OQServer.report (OQServer.repDebug, "(In this version, not pre-loading dbForItems.)");
            // OQServer.report (OQServer.repDebug, "Loading dbForItems");
            // bTopic.loadItems (dbForItems);
            // OQServer.report (OQServer.repDebug, "End of While Loop");
          }
        } else {
          OQServer.report(OQServer.repError, "ERROR in OQTopic while creating connection to load topic items.");
        }
        dbForItems.CloseAccess();
        rs.close();


        /* Now load in paths.  It's permissible to have more than 1 path
         * for one topic, so we need to do this in a separate loop.
         */
        sql = OQDBAccess.getSQLCommand(OQDBAccess.sql_SELECTTOPICPATHS);
        rs = aDB.getStatement().executeQuery( sql );
        OQServer.report (OQServer.repDebug, "Starting to process topic paths... ");
        while(rs.next()) {
          // For some strange reason, the JDBC-ODBC Bridge with
          // MSAccess only allow one read of a field value
          //
          long       rsTopicID2   = rs.getLong(1)   ;
          String     rsPath     = rs.getString(2) ;

          OQServer.report(OQServer.repDebug,
             "TopicID="    +rsTopicID2  + "   " +
             "Path="       +rsPath      );
          OQTopic bTopic = OQTopic.getByID((int) rsTopicID2);
          if (bTopic != null) {
            OQTopic.pathsHash.put(rsPath, bTopic);
          }

        }
        OQServer.report (OQServer.repDebug, "Done processing topic paths. ");
        rs.close();

      } catch( Exception e ) {
        OQServer.report(OQServer.repError, e.getMessage());
      }
    } /* synchronized */
  }

  static int totalTopics () {
    return OQTopics.size ();
  }


  public int nextVersion() {
    latestMessageID = latestMessageID + 1;
    return latestMessageID;
  }


  public OQTopic (OQServer aServer, int anID, String aTopicName, int aOwner, int aLastMsgID, int aFlag, int aReadFlags) {
    oqServer = aServer;
    topicID = anID;
    topicName = aTopicName;
    owner = aOwner;
    aboutURL = null;
    useAboutURLforItems = false;
    latestMessageID = aLastMsgID;
    flags = aFlag;
    if (aFlag == 1) {
      persistent = true;
    } else {
      persistent = false;
    }
    readFlags = aReadFlags;
    topicItems = new Vector (OQTopic.iDefaultTopicDBaseRowsAtStartup);

    OQTopics.addElement (this);
    OQServer.report(OQServer.repDebug, Long.toString(topicID) + " = " + topicName + "  current Topics = " + OQTopics.size ());
  }


  /*  topicPathExists() checks the database to see if that
   *  path is taken.
   *  Return 0 if it doesn't exist, else return the topicid.
   */
  public static int topicPathExists (OQServer anOQServer, String aTopicPath) {
    int iTopicID = 0;

    OQDBAccess aDB = anOQServer.GetDB();
    synchronized(aDB) {
      try {
        String sql = OQDBAccess.getSQLCommand(OQDBAccess.sql_SELECTTOPICBYPATH, aTopicPath);
        ResultSet rs = aDB.getStatement().executeQuery( sql );
        if (rs.next() == true)  {
          iTopicID =  (int) rs.getLong(1);
        }
        rs.close();
      catch (Exception e) {
        OQServer.report(OQServer.repError,  "Exception while trying to lookup topic path in database.");
        iTopicID = 0;
      }
    }
    return iTopicID;
  }

  // create topic in SQL database, then in memory
  // This is only to be called after initial server setup,
  // such as when administrator wants to create a new topic.

  public static boolean createNewTopic(OQServer anOQServer, String
     sTempTopicPath, String sTempTopicTitle, int iTempUser, int iPersistent, int iReadFlags) {

    boolean succeeded = false;
    OQDBAccess aDB = anOQServer.GetDB();
    synchronized(aDB) {
      try {
        String addTopicQuery1 = OQDBAccess.getSQLCommand(OQDBAccess.sql_CREATETOPIC,
          sTempTopicTitle,
          String.valueOf( iTempUser ),
          String.valueOf( iPersistent ),
          String.valueOf( iReadFlags   ) );

        String addTopicQuery2 = OQDBAccess.getSQLCommand(OQDBAccess.sql_SELECTMAXTOPICID);

        int updateResult = aDB.getStatement().executeUpdate( addTopicQuery1 );
        ResultSet rs = aDB.getStatement().executeQuery( addTopicQuery2 );
        if (rs.next()==true) {
          int rsTopicID = (int) rs.getLong(1);
          String addTopicQuery3 = OQDBAccess.getSQLCommand(OQDBAccess.sql_INSERTPATH,
            sTempTopicPath, String.valueOf( rsTopicID  ) );

          int updateResultPath = aDB.getStatement().executeUpdate( addTopicQuery3 );

          OQTopic tTempTopic = new OQTopic (anOQServer, rsTopicID, sTempTopicTitle, iTempUser, 0, iPersistent, iReadFlags) ;
          OQTopic.pathsHash.put(sTempTopicPath, tTempTopic);

          succeeded = true;
        }

      }  catch (Exception e) {
        OQServer.report(OQServer.repError,  "Exception while trying to add topic to database.");
        System.out.println(e.getMessage());
      }
    }
    return succeeded;



  }


  public synchronized void InsertSystemMessage(String Subject, String body) {
    OQServer.report(OQServer.repDebugMinor, "Inserting system message. " + Subject);
    OQItem theItem;
    String theHeaders = new String ("Subject: " + Subject);
    Date rightNow = new Date();
    theHeaders = theHeaders + "\r\n" + "Posted: " + rightNow.toGMTString();
    theItem = addItem(Subject, theHeaders, body);
  }

  public boolean userHasReadPermission(OQUser aUser) {
    if ((readFlags & 8) == 8) {  // world
      return true;
    } else {
      if ( ((readFlags & 1) == 1) && (owner==aUser.UserID) )   { // owner
        return true;
      } else {
        if ( ((readFlags & 2) == 2) && (aUser.isAdmin()==true) )   { // admin
          return true;
        } else {
          return false;
        }
      }
    }
  }

  public void loadItems (OQDBAccess newDB) {
    OQServer.report(OQServer.repNormal, "Pre-loading messages via database for topic " + topicID + ".");
      try {
          OQServer.report(OQServer.repDebugMinor, "Just entered try... " + topicID + ".");
          Statement NewStmt = newDB.getConnection().createStatement();
          String sql = OQDBAccess.getSQLCommand(OQDBAccess.sql_LOADMESSAGES, String.valueOf(topicID), String.valueOf(OQTopic.iDefaultTopicDBaseRowsAtStartup) );
          OQServer.report(OQServer.repDebugMinor, sql );
          ResultSet rsItems = NewStmt.executeQuery( sql );
          ResultSetMetaData rsmd = rsItems.getMetaData();
          int iColumnCount = rsmd.getColumnCount();
          if (rsmd.getColumnCount() != 4) {
            OQServer.report(OQServer.repError, "Wrong number of columns, some JDBC weirdness. Skipping this topic.");
          } else {

            Stack stk = new Stack();
            OQItem bItem = null;

            int iLoop_RowsDone = 0;
            while ((iLoop_RowsDone < OQTopic.iDefaultTopicDBaseRowsAtStartup) && (rsItems.next()) ) {
              OQServer.report(OQServer.repDebug, " msg... ");
              long aMessageID = 0;
              String aMessageBody = "";
              String aSubject = "";
              String aHeaders = "";
              boolean isMessageLoadedOK = true;
              try {
                aMessageID = rsItems.getLong(1);
                aMessageBody = rsItems.getString(2);
                aSubject = rsItems.getString(3);
                aHeaders = rsItems.getString(4);
              } catch (Exception ex) {

                /* for example, a null value for messagedata? */
                OQServer.report(OQServer.repNormal, "Error loading message for topic " + topicID + ": " + ex.getMessage());
                isMessageLoadedOK = false;
              }
              if (isMessageLoadedOK == true) {
                bItem = new OQItem ((int) aMessageID, aSubject );
                if (aHeaders != null) {
                  bItem.setHeaders ( aHeaders );
                }
                if (aMessageBody != null) {
                  bItem.setMessage ( aMessageBody );
                }
                stk.push(bItem);
              }
              iLoop_RowsDone++;
            }

            while (stk.empty() == false) {
              bItem = (OQItem) stk.pop();
              topicItems.addElement (bItem);
            }

          }
          OQServer.report(OQServer.repDebug, " - all items done for this topic -" );
          rsItems.close();
          NewStmt.close();
          OQServer.report(OQServer.repDebug, " - rsItems and NewStmt now closed for this topic. -" );
      } catch( Exception e ) {
        OQServer.report(OQServer.repError, e.getMessage());
      }
  }

  /* broadcast puts an item into the queues for all OQUpdaters
   * (all users currently active).
   */

  /* May 20, 2000 - MattJ:  Woah, deadlock issue here.
   * We're locking the updater here before locking the topic.
   * To be consistent with OQUser, we must lock topic, then updater.
   * So, I'm adding a lock on the topic first.
   */
  public synchronized void broadcast (OQItem theItem){
    OQUserCmdHandler aUserCmdHandler;

    Enumeration enum = OQUserCmdHandler.userCmdHandlers.elements ();

    while (enum.hasMoreElements ()) {
      try {
        aUserCmdHandler = (OQUserCmdHandler) enum.nextElement ();

        /* User might not be known yet if we just connected,
         * because we might be inserting a system message to
         * indicate that we just connected. So test for null.
         */

        /* !!!!! this next line seems to cause possible
           concurrency issue when it makes the getState call.
           Look into it!  (May 18, 2000) */
        if ((aUserCmdHandler.getState() != OQUserCmdHandler.state_UNAUTHORIZED) && (aUserCmdHandler.thisUser != null)) {
          if ( aUserCmdHandler.thisUser.getSubscriptionByID(topicID) != null ) {

            /* A user who is subscribed to this topic is "live" now,
             * with a UserCmdHandler handling the connection, so let's
             * stick this item in it's queue.
             */

            if (aUserCmdHandler.theUpdater != null) {
              // added next two lines to use proper lock order. MJ:2000/05/20
              OQTopic aTopic = OQTopic.getByID(topicID);
              synchronized (aTopic) {
                synchronized (aUserCmdHandler.theUpdater) {
                  aUserCmdHandler.theUpdater.addItem(topicID, theItem.getMessageID() );
                  aUserCmdHandler.theUpdater.notify();
                }
              }

            }    /* updater not null */

          }
        }
      } catch (Exception ex) {
        OQServer.report(OQServer.repError, "Error in broadcast loop. Bad next element?  " + ex.getMessage() );
      }
    }  /* while more connections to check */

  }

  public OQItem addItem(String aSubject, String theHeaders, String messageData) {

    /* First, check that content-length: exists */
    String upHeaders = theHeaders.toUpperCase();
    if (upHeaders.indexOf("\r\nCONTENT-LENGTH: ") == -1) {

      /* This header is missing, so let's add it. */
      theHeaders = theHeaders + "\r\nContent-Length: " + Integer.toString(messageData.length()) ;
    }
    OQItem bItem;
    OQItem resultItem = null;
    try {
      bItem = new OQItem (nextVersion(), aSubject);
      bItem.setHeaders (theHeaders ) ;
      bItem.setMessage (messageData);

      if (updateVersionInTopicsFile() == true) {
        if (storeMessagePermanently (bItem) == true) {
          synchronized (topicItems) {
            topicItems.addElement (bItem);
            while (topicItems.size() > iDefaultTopicMaxItemsInRAM ) {
              topicItems.removeElementAt(0);
            }
          }
          broadcast (bItem);

          // success, so return our new item.
          resultItem = bItem;
        }
      }
    } catch (Exception ex) {
    }
    return resultItem;
  }

  /* Record permanently that user has seen the message. */
  public synchronized boolean updateVersionInTopicsFile () {
    boolean success = false;
    if (persistent) {
      OQDBAccess aDB = oqServer.GetDB();
      synchronized (aDB) {
        String sql = OQDBAccess.getSQLCommand(OQDBAccess.sql_UPDATELASTMESSAGEID, String.valueOf(latestMessageID), String.valueOf(topicID) );
        success = aDB.doUpdate( sql );
      }
    } else {
      success = true;
    }
    return success;
  }

  // return true on success, false on failure
  public synchronized boolean storeMessagePermanently (OQItem anItem) {
    boolean success = true;
    if (persistent) {
      OQDBAccess aDB = oqServer.GetDB();
      synchronized(aDB) {
        try {
          String sql = OQDBAccess.getSQLCommand(OQDBAccess.sql_STOREMESSAGE,
            String.valueOf( topicID ),
            String.valueOf( anItem.getMessageID() ),
            anItem.getSubject(),
            String.valueOf( anItem.getMessageLength() ),
            anItem.getMessage(),
            anItem.getHeaders()  );

          success = aDB.doUpdate( sql );
        }   catch    (Exception ex) {
          success = false;
          OQServer.report(OQServer.repError, "Error in storeMessagePermanently: " + ex);
        }
      }
    }
    return success;
  }



  /* getByName() looks up an existing OQTopic, or returns null
   * if none match. */

  public static OQTopic getByName (String aName) {
    OQTopic aTopic;
    OQTopic result;

    result = null;

    synchronized (OQTopics) {
      Enumeration enum = OQTopics.elements ();
      while (enum.hasMoreElements ()) {
        aTopic = (OQTopic) enum.nextElement ();
        if ( aTopic.topicName.equals (aName)) result = aTopic;
      }
    }
    return result;
  }


  /* getByReference() receives a string which is either
   * a path ("/foo/bar") or a topic number ("#101").
   * This function determines which it is, and calls either
   * getByID() or getByName().
   */

  public static OQTopic getByReference (String sRef) {
    if (sRef.startsWith("#")) {
      // it's a number (unless there's an error)
      int aTopicID = -1;
      try {
        aTopicID = Integer.parseInt(sRef.substring(1,sRef.length()));
        return OQTopic.getByID(aTopicID);
      } catch (Exception ex) {
        return null;
      }
    } else {
      // it's a path
      return OQTopic.getByPath(sRef);
    }
  }

  public static OQTopic getByPath (String sPath) {
    OQTopic result;
    result = (OQTopic) OQTopic.pathsHash.get(sPath);
    return result;
  }


  /* getByID() looks up an existing OQTopic, or returns null if none match. */
  public static OQTopic getByID (int anID) {
    OQTopic aTopic;
    OQTopic result;
    result = null;
    synchronized (OQTopics) {
      Enumeration enum = OQTopics.elements ();
      while (enum.hasMoreElements ()) {
        aTopic = (OQTopic) enum.nextElement ();
        if ( aTopic.topicID == anID ) {
          result = aTopic;
        } else {
        }
      }
    }
    return result;
  }

  /* getItemByID() finds an existing OQItem for a given OQTopic,
   * or returns null if no match is found.
   */

  public OQItem getItemByID (int aMessageID) {
    OQItem tempItem;

    tempItem = null;
    if ( (aMessageID > 0) && (topicItems.size() > 0) ) {
      OQItem anItem;
      try {
        anItem = (OQItem) topicItems.elementAt(topicItems.size()-1);

        int latestInVector = anItem.getMessageID();
        if (aMessageID == latestInVector) {
          tempItem = (OQItem) topicItems.elementAt(topicItems.size()-1);
        } else {
          if (aMessageID > (latestInVector - topicItems.size()) ) {

            /* to do: add some smarts here like a boolean search
             * after a first-guess of where it should be.
             */
            Enumeration enumItems = topicItems.elements ();
            while (enumItems.hasMoreElements () && (tempItem == null) ) {
              anItem = (OQItem) enumItems.nextElement ();
              if ( anItem.getMessageID() == aMessageID ) {
                tempItem = anItem;
              }
            }
          } else {
            /* XXX - is outside current RAM vector.
             * We'll go on to do SQL query, below.
             */
          }
        }

      } catch (Exception ex) {
        tempItem = null;
      }

      if (tempItem == null) {

        /* Item isn't in memory. Let's see if it's in the data store. */
        OQDBAccess aDB = oqServer.GetDB();
        synchronized(aDB) {
          try {
            Statement NewStmt = aDB.getConnection().createStatement();
            String sql = OQDBAccess.getSQLCommand(OQDBAccess.sql_GETMESSAGE, String.valueOf(topicID), String.valueOf(aMessageID) );

            ResultSet rs = NewStmt.executeQuery( sql );

            if (rs.next()) {
              long ourMessageID = rs.getLong(1);
              String aMessageBody = rs.getString(2);
              String aSubject = rs.getString(3);
              String aHeaders = rs.getString(4);

              tempItem = new OQItem (aMessageID, aSubject );
              if (aHeaders != null) {
                tempItem.setHeaders ( aHeaders );
              }
              if (aMessageBody != null) {
                tempItem.setMessage ( aMessageBody );
              }
              /* topicItems.addElement (bItem); */
            }
            rs.close();
          } catch( Exception e ) {
            OQServer.report(OQServer.repError, e.getMessage() );
          }
        } /* synchronized   */

      }
    }
    return tempItem;
  }
}
TOP

Related Classes of org.openqueue.oqtane.OQTopic

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.