1 # Copyright 2009 Shikhar Bhushan
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 from threading import Event
16 from Queue import Queue
18 from ncclient.capabilities import Capabilities
19 from ncclient.glue import Subject
21 from hello import HelloHandler
24 logger = logging.getLogger('ncclient.transport.session')
26 class Session(Thread):
30 def __init__(self, capabilities):
31 "Subclass constructor should call this"
34 self._listeners = set() # TODO(?) weakref
36 self.setName('session')
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))
45 def _dispatch_message(self, raw):
48 root = parse_root(raw)
49 except Exception as e:
50 logger.error('error parsing dispatch message: %s' % e)
53 listeners = list(self._listeners)
55 logger.debug('dispatching message to %r' % l)
58 except Exception as e:
59 logger.warning('[error] %r' % e)
61 def _dispatch_error(self, err):
64 listeners = list(self._listeners)
66 logger.debug('dispatching error to %r' % l)
69 except Exception as e:
70 logger.warning('error %r' % e)
72 def _post_connect(self):
75 error = [None] # so that err_cb can bind error[0]. just how it is.
77 def ok_cb(id, capabilities):
79 self._server_capabilities = Capabilities(capabilities)
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')
89 # we expect server's hello message
91 # received hello message or an error happened
92 self.remove_listener(listener)
95 logger.info('initialized: session-id=%s | server_capabilities=%s' %
96 (self._id, self._server_capabilities))
98 def add_listener(self, listener):
100 logger.debug('installing listener %r' % listener)
102 self._listeners.add(listener)
104 def remove_listener(self, listener):
106 logger.debug('discarding listener %r' % listener)
108 self._listeners.discard(listener)
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.
115 for listener in self._listeners:
116 if isinstance(listener, cls):
119 def connect(self, *args, **kwds):
120 "Subclass implements"
121 raise NotImplementedError
124 "Subclass implements"
125 raise NotImplementedError
127 def send(self, message):
129 logger.debug('queueing %s' % message)
135 def client_capabilities(self):
136 return self._client_capabilities
139 def server_capabilities(self):
140 return self._server_capabilities
144 return self._connected
151 def can_pipeline(self):