1
2import sys, time, os, random
3
4import transaction
5from persistent import Persistent
6
7from ZEO import ClientStorage
8import ZODB
9from ZODB.POSException import ConflictError
10from BTrees import OOBTree
11
12class ChatSession(Persistent):
13
14    """Class for a chat session.
15    Messages are stored in a B-tree, indexed by the time the message
16    was created.  (Eventually we'd want to throw messages out,
17
18    add_message(message) -- add a message to the channel
19    new_messages()       -- return new messages since the last call to
20                            this method
21
22
23    """
24
25    def __init__(self, name):
26        """Initialize new chat session.
27        name -- the channel's name
28        """
29
30        self.name = name
31
32        # Internal attribute: _messages holds all the chat messages.
33        self._messages = OOBTree.OOBTree()
34
35
36    def new_messages(self):
37        "Return new messages."
38
39        # self._v_last_time is the time of the most recent message
40        # returned to the user of this class.
41        if not hasattr(self, '_v_last_time'):
42            self._v_last_time = 0
43
44        new = []
45        T = self._v_last_time
46
47        for T2, message in self._messages.items():
48            if T2 > T:
49                new.append( message )
50                self._v_last_time = T2
51
52        return new
53
54    def add_message(self, message):
55        """Add a message to the channel.
56        message -- text of the message to be added
57        """
58
59        while 1:
60            try:
61                now = time.time()
62                self._messages[ now ] = message
63                transaction.commit()
64            except ConflictError:
65                # Conflict occurred; this process should abort,
66                # wait for a little bit, then try again.
67                transaction.abort()
68                time.sleep(.2)
69            else:
70                # No ConflictError exception raised, so break
71                # out of the enclosing while loop.
72                break
73        # end while
74
75def get_chat_session(conn, channelname):
76    """Return the chat session for a given channel, creating the session
77    if required."""
78
79    # We'll keep a B-tree of sessions, mapping channel names to
80    # session objects.  The B-tree is stored at the ZODB's root under
81    # the key 'chat_sessions'.
82    root = conn.root()
83    if not root.has_key('chat_sessions'):
84        print 'Creating chat_sessions B-tree'
85        root['chat_sessions'] = OOBTree.OOBTree()
86        transaction.commit()
87
88    sessions = root['chat_sessions']
89
90    # Get a session object corresponding to the channel name, creating
91    # it if necessary.
92    if not sessions.has_key( channelname ):
93        print 'Creating new session:', channelname
94        sessions[ channelname ] = ChatSession(channelname)
95        transaction.commit()
96
97    session = sessions[ channelname ]
98    return session
99
100
101if __name__ == '__main__':
102    if len(sys.argv) != 2:
103        print 'Usage: %s <channelname>' % sys.argv[0]
104        sys.exit(0)
105
106    storage = ClientStorage.ClientStorage( ('localhost', 9672) )
107    db = ZODB.DB( storage )
108    conn = db.open()
109
110    s = session = get_chat_session(conn, sys.argv[1])
111
112    messages = ['Hi.', 'Hello', 'Me too', "I'M 3L33T!!!!"]
113
114    while 1:
115        # Send a random message
116        msg = random.choice(messages)
117        session.add_message( '%s: pid %i' % (msg,os.getpid() ))
118
119        # Display new messages
120        for msg in session.new_messages():
121            print msg
122
123        # Wait for a few seconds
124        pause = random.randint( 1, 4 )
125        time.sleep( pause )
126