~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/server.py

  • Committer: Jonathan Riddell
  • Date: 2011-09-20 11:46:28 UTC
  • mto: This revision was merged to the branch mainline in revision 6153.
  • Revision ID: jriddell@canonical.com-20110920114628-o8idd3n4eb4v3n32
fix order of imports

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
22
22
import sys
23
23
import threading
24
24
 
25
 
from bzrlib.hooks import HookPoint, Hooks
 
25
from bzrlib.hooks import Hooks
26
26
from bzrlib import (
27
27
    errors,
28
28
    trace,
29
 
    transport,
 
29
    transport as _mod_transport,
30
30
)
 
31
from bzrlib.i18n import gettext
31
32
from bzrlib.lazy_import import lazy_import
32
33
lazy_import(globals(), """
33
34
from bzrlib.smart import medium
34
35
from bzrlib.transport import (
35
36
    chroot,
36
 
    get_transport,
37
37
    pathfilter,
38
38
    )
39
39
from bzrlib import (
51
51
    hooks: An instance of SmartServerHooks.
52
52
    """
53
53
 
54
 
    def __init__(self, backing_transport, host='127.0.0.1', port=0,
55
 
                 root_client_path='/'):
 
54
    def __init__(self, backing_transport, root_client_path='/'):
56
55
        """Construct a new server.
57
56
 
58
57
        To actually start it running, call either start_background_thread or
59
58
        serve.
60
59
 
61
60
        :param backing_transport: The transport to serve.
 
61
        :param root_client_path: The client path that will correspond to root
 
62
            of backing_transport.
 
63
        """
 
64
        self.backing_transport = backing_transport
 
65
        self.root_client_path = root_client_path
 
66
 
 
67
    def start_server(self, host, port):
 
68
        """Create the server listening socket.
 
69
 
62
70
        :param host: Name of the interface to listen on.
63
71
        :param port: TCP port to listen on, or 0 to allocate a transient port.
64
 
        :param root_client_path: The client path that will correspond to root
65
 
            of backing_transport.
66
72
        """
67
73
        # let connections timeout so that we get a chance to terminate
68
74
        # Keep a reference to the exceptions we want to catch because the socket
89
95
        self.port = self._sockname[1]
90
96
        self._server_socket.listen(1)
91
97
        self._server_socket.settimeout(1)
92
 
        self.backing_transport = backing_transport
93
98
        self._started = threading.Event()
94
99
        self._stopped = threading.Event()
95
 
        self.root_client_path = root_client_path
96
100
 
97
 
    def serve(self, thread_name_suffix=''):
98
 
        self._should_terminate = False
99
 
        # for hooks we are letting code know that a server has started (and
100
 
        # later stopped).
 
101
    def _backing_urls(self):
101
102
        # There are three interesting urls:
102
103
        # The URL the server can be contacted on. (e.g. bzr://host/)
103
104
        # The URL that a commit done on the same machine as the server will
104
105
        # have within the servers space. (e.g. file:///home/user/source)
105
106
        # The URL that will be given to other hooks in the same process -
106
 
        # the URL of the backing transport itself. (e.g. chroot+:///)
 
107
        # the URL of the backing transport itself. (e.g. filtered-36195:///)
107
108
        # We need all three because:
108
109
        #  * other machines see the first
109
110
        #  * local commits on this machine should be able to be mapped to
113
114
        # The latter two urls are different aliases to the servers url,
114
115
        # so we group those in a list - as there might be more aliases
115
116
        # in the future.
116
 
        backing_urls = [self.backing_transport.base]
 
117
        urls = [self.backing_transport.base]
117
118
        try:
118
 
            backing_urls.append(self.backing_transport.external_url())
 
119
            urls.append(self.backing_transport.external_url())
119
120
        except errors.InProcessTransport:
120
121
            pass
 
122
        return urls
 
123
 
 
124
    def run_server_started_hooks(self, backing_urls=None):
 
125
        if backing_urls is None:
 
126
            backing_urls = self._backing_urls()
121
127
        for hook in SmartTCPServer.hooks['server_started']:
122
128
            hook(backing_urls, self.get_url())
123
129
        for hook in SmartTCPServer.hooks['server_started_ex']:
124
130
            hook(backing_urls, self)
 
131
 
 
132
    def run_server_stopped_hooks(self, backing_urls=None):
 
133
        if backing_urls is None:
 
134
            backing_urls = self._backing_urls()
 
135
        for hook in SmartTCPServer.hooks['server_stopped']:
 
136
            hook(backing_urls, self.get_url())
 
137
 
 
138
    def serve(self, thread_name_suffix=''):
 
139
        self._should_terminate = False
 
140
        # for hooks we are letting code know that a server has started (and
 
141
        # later stopped).
 
142
        self.run_server_started_hooks()
125
143
        self._started.set()
126
144
        try:
127
145
            try:
155
173
            except self._socket_error:
156
174
                # ignore errors on close
157
175
                pass
158
 
            for hook in SmartTCPServer.hooks['server_stopped']:
159
 
                hook(backing_urls, self.get_url())
 
176
            self.run_server_stopped_hooks()
160
177
 
161
178
    def get_url(self):
162
179
        """Return the url of the server"""
163
 
        return "bzr://%s:%d/" % self._sockname
 
180
        return "bzr://%s:%s/" % (self._sockname[0], self._sockname[1])
164
181
 
165
182
    def serve_conn(self, conn, thread_name_suffix):
166
183
        # For WIN32, where the timeout value from the listening socket
172
189
        thread_name = 'smart-server-child' + thread_name_suffix
173
190
        connection_thread = threading.Thread(
174
191
            None, handler.serve, name=thread_name)
 
192
        # FIXME: This thread is never joined, it should at least be collected
 
193
        # somewhere so that tests that want to check for leaked threads can get
 
194
        # rid of them -- vila 20100531
175
195
        connection_thread.setDaemon(True)
176
196
        connection_thread.start()
 
197
        return connection_thread
177
198
 
178
199
    def start_background_thread(self, thread_name_suffix=''):
179
200
        self._started.clear()
219
240
        These are all empty initially, because by default nothing should get
220
241
        notified.
221
242
        """
222
 
        Hooks.__init__(self)
223
 
        self.create_hook(HookPoint('server_started',
 
243
        Hooks.__init__(self, "bzrlib.smart.server", "SmartTCPServer.hooks")
 
244
        self.add_hook('server_started',
224
245
            "Called by the bzr server when it starts serving a directory. "
225
246
            "server_started is called with (backing urls, public url), "
226
247
            "where backing_url is a list of URLs giving the "
227
248
            "server-specific directory locations, and public_url is the "
228
 
            "public URL for the directory being served.", (0, 16), None))
229
 
        self.create_hook(HookPoint('server_started_ex',
 
249
            "public URL for the directory being served.", (0, 16))
 
250
        self.add_hook('server_started_ex',
230
251
            "Called by the bzr server when it starts serving a directory. "
231
252
            "server_started is called with (backing_urls, server_obj).",
232
 
            (1, 17), None))
233
 
        self.create_hook(HookPoint('server_stopped',
 
253
            (1, 17))
 
254
        self.add_hook('server_stopped',
234
255
            "Called by the bzr server when it stops serving a directory. "
235
256
            "server_stopped is called with the same parameters as the "
236
 
            "server_started hook: (backing_urls, public_url).", (0, 16), None))
 
257
            "server_started hook: (backing_urls, public_url).", (0, 16))
 
258
        self.add_hook('server_exception',
 
259
            "Called by the bzr server when an exception occurs. "
 
260
            "server_exception is called with the sys.exc_info() tuple "
 
261
            "return true for the hook if the exception has been handled, "
 
262
            "in which case the server will exit normally.", (2, 4))
237
263
 
238
264
SmartTCPServer.hooks = SmartServerHooks()
239
265
 
305
331
        chroot_server = chroot.ChrootServer(transport)
306
332
        chroot_server.start_server()
307
333
        self.cleanups.append(chroot_server.stop_server)
308
 
        transport = get_transport(chroot_server.get_url())
 
334
        transport = _mod_transport.get_transport_from_url(chroot_server.get_url())
309
335
        if self.base_path is not None:
310
336
            # Decorate the server's backing transport with a filter that can
311
337
            # expand homedirs.
312
338
            expand_userdirs = self._make_expand_userdirs_filter(transport)
313
339
            expand_userdirs.start_server()
314
340
            self.cleanups.append(expand_userdirs.stop_server)
315
 
            transport = get_transport(expand_userdirs.get_url())
 
341
            transport = _mod_transport.get_transport_from_url(expand_userdirs.get_url())
316
342
        self.transport = transport
317
343
 
318
344
    def _make_smart_server(self, host, port, inet):
324
350
                host = medium.BZR_DEFAULT_INTERFACE
325
351
            if port is None:
326
352
                port = medium.BZR_DEFAULT_PORT
327
 
            smart_server = SmartTCPServer(self.transport, host=host, port=port)
328
 
            trace.note('listening on port: %s' % smart_server.port)
 
353
            smart_server = SmartTCPServer(self.transport)
 
354
            smart_server.start_server(host, port)
 
355
            trace.note(gettext('listening on port: %s') % smart_server.port)
329
356
        self.smart_server = smart_server
330
357
 
331
358
    def _change_globals(self):
352
379
        for cleanup in reversed(self.cleanups):
353
380
            cleanup()
354
381
 
355
 
 
356
382
def serve_bzr(transport, host=None, port=None, inet=False):
357
383
    """This is the default implementation of 'bzr serve'.
358
384
    
364
390
    try:
365
391
        bzr_server.set_up(transport, host, port, inet)
366
392
        bzr_server.smart_server.serve()
 
393
    except:
 
394
        hook_caught_exception = False
 
395
        for hook in SmartTCPServer.hooks['server_exception']:
 
396
            hook_caught_exception = hook(sys.exc_info())
 
397
        if not hook_caught_exception:
 
398
            raise
367
399
    finally:
368
400
        bzr_server.tear_down()
369