~dustin-spy/twisted/dustin

« back to all changes in this revision

Viewing changes to twisted/application/service.py

  • Committer: moshez
  • Date: 2003-09-19 08:27:11 UTC
  • Revision ID: vcs-imports@canonical.com-20030919082711-7c4f41d51a909f9d
First stage of application integration

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Twisted, the Framework of Your Internet
 
2
# Copyright (C) 2001-2003 Matthew W. Lefkowitz
 
3
#
 
4
# This library is free software; you can redistribute it and/or
 
5
# modify it under the terms of version 2.1 of the GNU Lesser General Public
 
6
# License as published by the Free Software Foundation.
 
7
#
 
8
# This library is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
11
# Lesser General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU Lesser General Public
 
14
# License along with this library; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
#
 
17
"""Service architecture for Twisted
 
18
 
 
19
Services are arranged in a hierarchy. At the leafs of the hierarchy,
 
20
the services which actually interact with the outside world are started.
 
21
Services can be named or anonymous -- usually, they will be named if
 
22
there is need to access them through the hierarchy (from a parent or
 
23
a sibling).
 
24
 
 
25
API Stability: unstable
 
26
 
 
27
Maintainer: U{Moshe Zadka<mailto:moshez@twistedmatrix.com>}
 
28
"""
 
29
from twisted.python import components
 
30
from twisted.internet import defer
 
31
from twisted.persisted import sob
 
32
 
 
33
class IService(components.Interface):
 
34
 
 
35
    """
 
36
    A service.
 
37
 
 
38
    Run start-up and shut-down code at the appropriate times.
 
39
 
 
40
    @type name:            C{string}
 
41
    @ivar name:            The name of the service (or None)
 
42
    @type running:         C{boolean}
 
43
    @ivar running:         Whether the service is running.
 
44
    """
 
45
 
 
46
    def setName(self, name):
 
47
        """Set the name of the service.
 
48
 
 
49
        @type name: C{str}
 
50
        @raise L{RuntimeError}: Raised if the service already has a parent.
 
51
        """
 
52
 
 
53
    def setServiceParent(self, parent):
 
54
        """Set the parent of the service.
 
55
 
 
56
        @type name: C{IServiceCollection}
 
57
        @raise L{RuntimeError}: Raised if the service already has a parent
 
58
        or if the service has a name and the parent already has a child
 
59
        by that name.
 
60
        """
 
61
 
 
62
    def disownServiceParent(self):
 
63
        """Remove the parent of the service."""
 
64
 
 
65
    def startService(self):
 
66
        """Start the the service."""
 
67
 
 
68
    def stopService(self):
 
69
        """Stop the the service.
 
70
 
 
71
        @rtype: C{Deferred}
 
72
        @return: a deferred which is triggered when the service has
 
73
        finished shutting down. If shutting down is immediate,
 
74
        a value can be returned (usually, None).
 
75
        """
 
76
 
 
77
    def privilegedStartService(self):
 
78
        """Do preperation work for starting the service.
 
79
 
 
80
        Here things which should be done before changing directory,
 
81
        root or shedding privileges are done."""
 
82
 
 
83
 
 
84
class Service:
 
85
 
 
86
    """
 
87
    Base class for services
 
88
 
 
89
    Most services should inherit from this class. It handles the
 
90
    book-keeping reponsibilities of starting and stopping, as well
 
91
    as not serializing this book-keeping information.
 
92
    """
 
93
 
 
94
    __implements__ = IService,
 
95
 
 
96
    running = 0
 
97
    name = None
 
98
    parent = None
 
99
 
 
100
    def __getstate__(self):
 
101
        dict = self.__dict__.copy()
 
102
        if dict.has_key("running"):
 
103
            del dict['running']
 
104
        return dict
 
105
 
 
106
    def setName(self, name):
 
107
        if self.parent is not None:
 
108
            raise RuntimeError("cannot change name when parent exists")
 
109
        self.name = name
 
110
 
 
111
    def setServiceParent(self, parent):
 
112
        if self.parent is not None:
 
113
            self.disownServiceParent()
 
114
        self.parent = parent
 
115
        self.parent.addService(self)
 
116
 
 
117
    def disownServiceParent(self):
 
118
        self.parent.removeService(self)
 
119
        self.parent = None
 
120
 
 
121
    def privilegedStartService(self):
 
122
        pass
 
123
 
 
124
    def startService(self):
 
125
        self.running = 1
 
126
 
 
127
    def stopService(self):
 
128
        self.running = 0
 
129
 
 
130
 
 
131
class IServiceCollection(components.Interface):
 
132
 
 
133
    """Collection of services.
 
134
 
 
135
    Contain several services, and manage their start-up/shut-down.
 
136
    Services can be accessed by name if they have a name, and it
 
137
    is always possible to iterate over them.
 
138
    """
 
139
    
 
140
    def getServiceNamed(self, name):
 
141
        """Get the child service with a given name.
 
142
 
 
143
        @type name: C{str}
 
144
        @rtype: C{IService}
 
145
        @raise L{KeyError}: Raised if the service has no child with the
 
146
        given name.
 
147
        """
 
148
 
 
149
    def __iter__(self):
 
150
        """Get an iterator over all child services"""
 
151
 
 
152
    def addService(self, service):
 
153
         """Add a child service.
 
154
 
 
155
        @type service: C{IService}
 
156
        @raise L{RuntimeError}: Raised if the service has a child with
 
157
        the given name.
 
158
        """
 
159
 
 
160
    def removeService(self, service):
 
161
        """Remove a child service.
 
162
        
 
163
        @type service: C{IService}
 
164
        @raise L{ValueError}: Raised if the given service is not a child.
 
165
        """
 
166
 
 
167
 
 
168
class MultiService(Service):
 
169
 
 
170
    """Straightforward Service Container
 
171
 
 
172
    Hold a collection of services, and manage them in a simplistic
 
173
    way. No service will wait for another, but this object itself
 
174
    will not finish shutting down until all of its child services
 
175
    will finish.
 
176
    """
 
177
 
 
178
    __implements__ = Service.__implements__, IServiceCollection
 
179
 
 
180
    def __init__(self):
 
181
        self.services = []
 
182
        self.namedServices = {}
 
183
        self.parent = None
 
184
 
 
185
    def privilegedStartService(self):
 
186
        Service.privilegedStartService(self)
 
187
        for service in self:
 
188
            service.privilegedStartService()
 
189
 
 
190
    def startService(self):
 
191
        Service.startService(self)
 
192
        for service in self:
 
193
            service.startService()
 
194
 
 
195
    def stopService(self):
 
196
        Service.stopService(self)
 
197
        l = []
 
198
        for service in self:
 
199
            l.append(defer.maybeDeferred(service.stopService))
 
200
        return defer.DeferredList(l)
 
201
 
 
202
    def getServiceNamed(self, name):
 
203
        return self.namedServices[name]
 
204
 
 
205
    def __iter__(self):
 
206
        return iter(self.services)
 
207
 
 
208
    def addService(self, service):
 
209
        if service.name is not None:
 
210
            if self.namedServices.has_key(service.name):
 
211
                raise RuntimeError("cannot have two services with same name")
 
212
            self.namedServices[service.name] = service
 
213
        self.services.append(service)
 
214
        if self.running:
 
215
            service.startService()
 
216
 
 
217
    def removeService(self, service):
 
218
        if service.name:
 
219
            del self.namedServices[service.name]
 
220
        self.services.remove(service)
 
221
        if self.running:
 
222
            service.stopService()
 
223
 
 
224
 
 
225
class IProcess(components.Interface):
 
226
 
 
227
    """Process running parameters
 
228
 
 
229
    Represents parameters for how processes should be run.
 
230
 
 
231
    @ivar processName: the name the process should have in ps (or None)
 
232
    @type processName: C{str}
 
233
    @ivar uid: the user-id the process should run under.
 
234
    @type uid: C{int}
 
235
    @ivar gid: the group-id the process should run under.
 
236
    @type gid: C{int}
 
237
    """
 
238
 
 
239
 
 
240
class Process:
 
241
 
 
242
    """Process running parameters
 
243
 
 
244
    Sets up uid/gid in the constructor, and has a default
 
245
    of C{None} as C{processName}.
 
246
    """
 
247
    __implements__ = IProcess,
 
248
    processName = None
 
249
 
 
250
    def __init__(self, uid=None, gid=None):
 
251
        """Set uid and gid
 
252
 
 
253
        By default, uid or gid will be 0 (superuser)
 
254
        """
 
255
        self.uid = uid or 0
 
256
        self.gid = gid or 0
 
257
    
 
258
 
 
259
def Application(name, uid=None, gid=None):
 
260
    """Return a compound class.
 
261
 
 
262
    Return an object supporting the C{IService}, C{IServiceCollection},
 
263
    C{IProcess} and C{sob.IPersistable} interfaces, with the given
 
264
    parameters. Always access the return value by explicit casting to
 
265
    one of the interfaces.
 
266
    """
 
267
    ret = components.Componentized()
 
268
    for comp in (MultiService(), sob.Persistant(ret, name), Process(uid, gid)):
 
269
        ret.addComponent(comp, ignoreClass=1)
 
270
    IService(ret).setName(name)
 
271
    return ret
 
272
 
 
273
def loadApplication(filename, kind, passphrase=None):
 
274
    """Load Application from file
 
275
 
 
276
    @type filename: C{str}
 
277
    @type kind: C{str}
 
278
    @type passphrase: C{str}
 
279
 
 
280
    Load application from a given file. The serialization format it
 
281
    was saved in should be given as C{kind}, and is one of 'pickle', 'source',
 
282
    'xml' or 'python'. If C{passphrase} is given, the application was encrypted
 
283
    with the given passphrase.
 
284
    """
 
285
    if kind == 'python':
 
286
        application = sob.loadValueFromFile(filename, 'application', passphrase)
 
287
    else:
 
288
        application = sob.load(filename, kind, passphrase)
 
289
    if IService(application, None) is None:
 
290
        from twisted.application import compat
 
291
        application = compat.convert(application)
 
292
    return application
 
293
 
 
294
__all__ = ['IService', 'Service', 'IServiceCollection', 'MultiService',
 
295
           'IProcess', 'Process', 'Application', 'loadApplication']