Statistics
| Branch: | Tag: | Revision:

root / ncclient / transport / session.py @ 65c6a607

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 threading import Event
16
from Queue import Queue
17

    
18
from ncclient.capabilities import Capabilities
19
from ncclient.glue import Subject
20

    
21
from hello import HelloHandler
22

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

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

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