1"""Abstract servlets"""
2
3import os
4
5from MiscUtils import AbstractError
6from MiscUtils.Funcs import asclocaltime, excstr
7
8
9class Servlet(object):
10    """A general servlet.
11
12    A servlet is a key portion of a server-based application that implements
13    the semantics of a particular request by providing a response.
14    This abstract class defines servlets at a very high level.
15    Most often, developers will subclass HTTPServlet or even Page.
16
17    Servlets can be created once, then used and destroyed, or they may be
18    reused several times over (it's up to the server). Therefore, servlet
19    developers should take the proper actions in awake() and sleep()
20    so that reuse can occur.
21
22    Objects that participate in a transaction include:
23      * Application
24      * Request
25      * Transaction
26      * Session
27      * Servlet
28      * Response
29
30    The awake(), respond() and sleep() methods form a message sandwich.
31    Each is passed an instance of Transaction which gives further access
32    to all the objects involved.
33    """
34
35
36    ## Init ##
37
38    def __init__(self):
39        """Subclasses must invoke super."""
40        self._serverSidePath = None
41        self._factory = None
42        self._busy = False
43
44
45    ## Access ##
46
47    def name(self):
48        """Return the name which is simple the name of the class.
49
50        Subclasses should *not* override this method.
51        It is used for logging and debugging.
52        """
53        return self.__class__.__name__
54
55
56    ## Request-response cycles ##
57
58    @staticmethod
59    def runTransaction(trans):
60        try:
61            trans.awake()
62            trans.respond()
63        except Exception as first:
64            try:
65                trans.sleep()
66            except Exception as second:
67                # The first exception is more important than the *second* one
68                # that comes from sleep(). In fact, without this little trick
69                # the first exception gets hidden by the second which is often
70                # just a result of the first. Then you're stuck scratching your
71                # head wondering what the first might have been.
72                raise Exception('Two exceptions. first=%s; second=%s'
73                    % (excstr(first), excstr(second)))
74            else:
75                raise  # no problems with sleep() so raise the one and only exception
76        else:
77            trans.sleep()
78
79    def runMethodForTransaction(self, trans, method, *args, **kw):
80        self.awake(trans)
81        result = getattr(self, method)(*args, **kw)
82        self.sleep(trans)
83        return result
84
85    def awake(self, trans):
86        """Send the awake message.
87
88        This message is sent to all objects that participate in the
89        request-response cycle in a top-down fashion, prior to respond().
90        Subclasses must invoke super.
91        """
92        self._transaction = trans
93
94    def respond(self, trans):
95        """Respond to a request."""
96        raise AbstractError(self.__class__)
97
98    def sleep(self, trans):
99        """Send the sleep message."""
100        pass
101
102
103    ## Log ##
104
105    def log(self, message):
106        """Log a message.
107
108        This can be invoked to print messages concerning the servlet.
109        This is often used by self to relay important information back
110        to developers.
111        """
112        print '[%s] [msg] %s' % (asclocaltime(), message)
113
114
115    ## Abilities ##
116
117    @staticmethod
118    def canBeThreaded():
119        """Return whether the servlet can be multithreaded.
120
121        This value should not change during the lifetime of the object.
122        The default implementation returns False.
123        Note: This is not currently used.
124        """
125        return False
126
127    @staticmethod
128    def canBeReused():
129        """Returns whether a single servlet instance can be reused.
130
131        The default is True, but subclasses con override to return False.
132        Keep in mind that performance may seriously be degraded if instances
133        can't be reused. Also, there's no known good reasons not to reuse
134        an instance. Remember the awake() and sleep() methods are invoked
135        for every transaction. But just in case, your servlet can refuse
136        to be reused.
137        """
138        return True
139
140
141    ## Server side filesystem ##
142
143    def serverSidePath(self, path=None):
144        """Return the filesystem path of the page on the server."""
145        if self._serverSidePath is None:
146            self._serverSidePath = self._transaction.request().serverSidePath()
147        if path:
148            if path.startswith('/'):
149                path = path[1:]
150            return os.path.normpath(os.path.join(
151                os.path.dirname(self._serverSidePath), path))
152        else:
153            return self._serverSidePath
154
155
156    ## Private ##
157
158    def open(self):
159        self._busy = True
160
161    def close(self):
162        if self._busy and self._factory:
163            self._busy = False
164            self._factory.returnServlet(self)
165
166    def setFactory(self, factory):
167        self._factory = factory
168