1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
5 Service architecture for Twisted.
7 Services are arranged in a hierarchy. At the leafs of the hierarchy,
8 the services which actually interact with the outside world are started.
9 Services can be named or anonymous -- usually, they will be named if
10 there is need to access them through the hierarchy (from a parent or
13 Maintainer: Moshe Zadka
16 from zope.interface import implements, Interface, Attribute
18 from twisted.python.reflect import namedAny
19 from twisted.python import components
20 from twisted.internet import defer
21 from twisted.persisted import sob
22 from twisted.plugin import IPlugin
25 class IServiceMaker(Interface):
27 An object which can be used to construct services in a flexible
30 This interface should most often be implemented along with
31 L{twisted.plugin.IPlugin}, and will most often be used by the
35 "A short string naming this Twisted plugin, for example 'web' or "
36 "'pencil'. This name will be used as the subcommand of 'twistd'.")
38 description = Attribute(
39 "A brief summary of the features provided by this "
40 "Twisted application plugin.")
43 "A C{twisted.python.usage.Options} subclass defining the "
44 "configuration options for this application.")
47 def makeService(options):
49 Create and return an object providing
50 L{twisted.application.service.IService}.
52 @param options: A mapping (typically a C{dict} or
53 L{twisted.python.usage.Options} instance) of configuration
54 options to desired configuration values.
59 class ServiceMaker(object):
61 Utility class to simplify the definition of L{IServiceMaker} plugins.
63 implements(IPlugin, IServiceMaker)
65 def __init__(self, name, module, description, tapname):
68 self.description = description
69 self.tapname = tapname
74 return namedAny(self.module).Options
76 options = property(*options())
81 return namedAny(self.module).makeService
83 makeService = property(*makeService())
87 class IService(Interface):
91 Run start-up and shut-down code at the appropriate times.
94 @ivar name: The name of the service (or None)
95 @type running: C{boolean}
96 @ivar running: Whether the service is running.
101 Set the name of the service.
104 @raise RuntimeError: Raised if the service already has a parent.
107 def setServiceParent(parent):
109 Set the parent of the service. This method is responsible for setting
110 the C{parent} attribute on this service (the child service).
112 @type parent: L{IServiceCollection}
113 @raise RuntimeError: Raised if the service already has a parent
114 or if the service has a name and the parent already has a child
118 def disownServiceParent():
120 Use this API to remove an L{IService} from an L{IServiceCollection}.
122 This method is used symmetrically with L{setServiceParent} in that it
123 sets the C{parent} attribute on the child.
125 @rtype: L{Deferred<defer.Deferred>}
126 @return: a L{Deferred<defer.Deferred>} which is triggered when the
127 service has finished shutting down. If shutting down is immediate,
128 a value can be returned (usually, C{None}).
140 @rtype: L{Deferred<defer.Deferred>}
141 @return: a L{Deferred<defer.Deferred>} which is triggered when the
142 service has finished shutting down. If shutting down is immediate,
143 a value can be returned (usually, C{None}).
146 def privilegedStartService():
148 Do preparation work for starting the service.
150 Here things which should be done before changing directory,
151 root or shedding privileges are done.
157 Base class for services.
159 Most services should inherit from this class. It handles the
160 book-keeping reponsibilities of starting and stopping, as well
161 as not serializing this book-keeping information.
170 def __getstate__(self):
171 dict = self.__dict__.copy()
172 if dict.has_key("running"):
176 def setName(self, name):
177 if self.parent is not None:
178 raise RuntimeError("cannot change name when parent exists")
181 def setServiceParent(self, parent):
182 if self.parent is not None:
183 self.disownServiceParent()
184 parent = IServiceCollection(parent, parent)
186 self.parent.addService(self)
188 def disownServiceParent(self):
189 d = self.parent.removeService(self)
193 def privilegedStartService(self):
196 def startService(self):
199 def stopService(self):
204 class IServiceCollection(Interface):
206 Collection of services.
208 Contain several services, and manage their start-up/shut-down.
209 Services can be accessed by name if they have a name, and it
210 is always possible to iterate over them.
213 def getServiceNamed(name):
215 Get the child service with a given name.
219 @raise KeyError: Raised if the service has no child with the
225 Get an iterator over all child services.
228 def addService(service):
232 Only implementations of L{IService.setServiceParent} should use this
235 @type service: L{IService}
236 @raise RuntimeError: Raised if the service has a child with
240 def removeService(service):
242 Remove a child service.
244 Only implementations of L{IService.disownServiceParent} should
247 @type service: L{IService}
248 @raise ValueError: Raised if the given service is not a child.
249 @rtype: L{Deferred<defer.Deferred>}
250 @return: a L{Deferred<defer.Deferred>} which is triggered when the
251 service has finished shutting down. If shutting down is immediate,
252 a value can be returned (usually, C{None}).
257 class MultiService(Service):
259 Straightforward Service Container.
261 Hold a collection of services, and manage them in a simplistic
262 way. No service will wait for another, but this object itself
263 will not finish shutting down until all of its child services
267 implements(IServiceCollection)
271 self.namedServices = {}
274 def privilegedStartService(self):
275 Service.privilegedStartService(self)
277 service.privilegedStartService()
279 def startService(self):
280 Service.startService(self)
282 service.startService()
284 def stopService(self):
285 Service.stopService(self)
287 services = list(self)
289 for service in services:
290 l.append(defer.maybeDeferred(service.stopService))
291 return defer.DeferredList(l)
293 def getServiceNamed(self, name):
294 return self.namedServices[name]
297 return iter(self.services)
299 def addService(self, service):
300 if service.name is not None:
301 if self.namedServices.has_key(service.name):
302 raise RuntimeError("cannot have two services with same name"
303 " '%s'" % service.name)
304 self.namedServices[service.name] = service
305 self.services.append(service)
307 # It may be too late for that, but we will do our best
308 service.privilegedStartService()
309 service.startService()
311 def removeService(self, service):
313 del self.namedServices[service.name]
314 self.services.remove(service)
316 # Returning this so as not to lose information from the
317 # MultiService.stopService deferred.
318 return service.stopService()
324 class IProcess(Interface):
326 Process running parameters.
328 Represents parameters for how processes should be run.
330 processName = Attribute(
332 A C{str} giving the name the process should have in ps (or C{None}
333 to leave the name alone).
338 An C{int} giving the user id as which the process should run (or
339 C{None} to leave the UID alone).
344 An C{int} giving the group id as which the process should run (or
345 C{None} to leave the GID alone).
352 Process running parameters.
354 Sets up uid/gid in the constructor, and has a default
355 of C{None} as C{processName}.
360 def __init__(self, uid=None, gid=None):
364 @param uid: The user ID as whom to execute the process. If
365 this is C{None}, no attempt will be made to change the UID.
367 @param gid: The group ID as whom to execute the process. If
368 this is C{None}, no attempt will be made to change the GID.
374 def Application(name, uid=None, gid=None):
376 Return a compound class.
378 Return an object supporting the L{IService}, L{IServiceCollection},
379 L{IProcess} and L{sob.IPersistable} interfaces, with the given
380 parameters. Always access the return value by explicit casting to
381 one of the interfaces.
383 ret = components.Componentized()
384 for comp in (MultiService(), sob.Persistent(ret, name), Process(uid, gid)):
385 ret.addComponent(comp, ignoreClass=1)
386 IService(ret).setName(name)
391 def loadApplication(filename, kind, passphrase=None):
393 Load Application from a given file.
395 The serialization format it was saved in should be given as
396 C{kind}, and is one of C{pickle}, C{source}, C{xml} or C{python}. If
397 C{passphrase} is given, the application was encrypted with the
400 @type filename: C{str}
402 @type passphrase: C{str}
405 application = sob.loadValueFromFile(filename, 'application', passphrase)
407 application = sob.load(filename, kind, passphrase)
411 __all__ = ['IServiceMaker', 'IService', 'Service',
412 'IServiceCollection', 'MultiService',
413 'IProcess', 'Process', 'Application', 'loadApplication']