Statistics
| Branch: | Tag: | Revision:

root / ncclient / transport / session.py @ 4de03d63

History | View | Annotate | Download (4.7 kB)

1
# Copyright 2009 Shikhar Bhushan
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License");
4
# you may not use this file except in compliance with the License.
5
# You may obtain a copy of the License at
6
#
7
#    http://www.apache.org/licenses/LICENSE-2.0
8
#
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS,
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
# See the License for the specific language governing permissions and
13
# limitations under the License.
14

    
15
from Queue import Queue
16
from threading import Thread, Lock, Event
17

    
18
from ncclient.capabilities import Capabilities
19
from ncclient.content import parse_root
20

    
21
from hello import HelloHandler
22

    
23
import logging
24
logger = logging.getLogger('ncclient.transport.session')
25

    
26

    
27
class Session(Thread):
28
    
29
    "TODO: docstring"
30
    
31
    def __init__(self, capabilities):
32
        "Subclass constructor should call this"
33
        Thread.__init__(self)
34
        self.setDaemon(True)
35
        self._listeners = set() # TODO(?) weakref
36
        self._lock = Lock()
37
        self.setName('session')
38
        self._q = Queue()
39
        self._client_capabilities = capabilities
40
        self._server_capabilities = None # yet
41
        self._id = None # session-id
42
        self._connected = False # to be set/cleared by subclass implementation
43
        logger.debug('%r created: client_capabilities=%r' %
44
                     (self, self._client_capabilities))
45
    
46
    def _dispatch_message(self, raw):
47
        "TODO: docstring"
48
        try:
49
            root = parse_root(raw)
50
        except Exception as e:
51
            logger.error('error parsing dispatch message: %s' % e)
52
            return
53
        with self._lock:
54
            listeners = list(self._listeners)
55
        for l in listeners:
56
            logger.debug('dispatching message to %r' % l)
57
            try:
58
                l.callback(root, raw)
59
            except Exception as e:
60
                logger.warning('[error] %r' % e)
61
    
62
    def _dispatch_error(self, err):
63
        "TODO: docstring"
64
        with self._lock:
65
            listeners = list(self._listeners)
66
        for l in listeners:
67
            logger.debug('dispatching error to %r' % l)
68
            try:
69
                l.errback(err)
70
            except Exception as e:
71
                logger.warning('error %r' % e)
72
    
73
    def _post_connect(self):
74
        "Greeting stuff"
75
        init_event = Event()
76
        error = [None] # so that err_cb can bind error[0]. just how it is.
77
        # callbacks
78
        def ok_cb(id, capabilities):
79
            self._id = id
80
            self._server_capabilities = Capabilities(capabilities)
81
            init_event.set()
82
        def err_cb(err):
83
            error[0] = err
84
            init_event.set()
85
        listener = HelloHandler(ok_cb, err_cb)
86
        self.add_listener(listener)
87
        self.send(HelloHandler.build(self._client_capabilities))
88
        logger.debug('starting main loop')
89
        self.start()
90
        # we expect server's hello message
91
        init_event.wait()
92
        # received hello message or an error happened
93
        self.remove_listener(listener)
94
        if error[0]:
95
            raise error[0]
96
        logger.info('initialized: session-id=%s | server_capabilities=%s' %
97
                     (self._id, self._server_capabilities))
98
    
99
    def add_listener(self, listener):
100
        "TODO: docstring"
101
        logger.debug('installing listener %r' % listener)
102
        with self._lock:
103
            self._listeners.add(listener)
104
    
105
    def remove_listener(self, listener):
106
        "TODO: docstring"
107
        logger.debug('discarding listener %r' % listener)
108
        with self._lock:
109
            self._listeners.discard(listener)
110
    
111
    def get_listener_instance(self, cls):
112
        '''This is useful when we want to maintain one listener of a particular
113
        type per subject i.e. a multiton.
114
        '''
115
        with self._lock:
116
            for listener in self._listeners:
117
                if isinstance(listener, cls):
118
                    return listener
119
    
120
    def connect(self, *args, **kwds):
121
        "Subclass implements"
122
        raise NotImplementedError
123

    
124
    def run(self):
125
        "Subclass implements"
126
        raise NotImplementedError
127
    
128
    def send(self, message):
129
        "TODO: docstring"
130
        logger.debug('queueing %s' % message)
131
        self._q.put(message)
132
    
133
    ### Properties
134
    
135
    @property
136
    def client_capabilities(self):
137
        return self._client_capabilities
138
    
139
    @property
140
    def server_capabilities(self):
141
        return self._server_capabilities
142
    
143
    @property
144
    def connected(self):
145
        return self._connected
146
    
147
    @property
148
    def id(self):
149
        return self._id
150
    
151
    @property
152
    def can_pipeline(self):
153
        return True