debuglistener
[ncclient] / ncclient / transport / session.py
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 Thread, Lock, Event
16 from Queue import Queue
17
18 from . import logger
19 from ncclient.capabilities import Capabilities, CAPABILITIES
20
21
22 class Subject:
23
24     def __init__(self):
25         self._listeners = set([])
26         self._lock = Lock()
27         
28     def has_listener(self, listener):
29         with self._lock:
30             return (listener in self._listeners)
31     
32     def add_listener(self, listener):
33         with self._lock:
34             self._listeners.add(listener)
35     
36     def remove_listener(self, listener):
37         with self._lock:
38             self._listeners.discard(listener)
39     
40     def dispatch(self, event, *args, **kwds):
41         # holding the lock while doing callbacks could lead to a deadlock
42         # if one of the above methods is called
43         with self._lock:
44             listeners = list(self._listeners)
45         for l in listeners:
46             try:
47                 logger.debug('dispatching [%s] to [%s]' % (event, l))
48                 getattr(l, event)(*args, **kwds)
49             except Exception as e:
50                 pass # if a listener doesn't care for some event we don't care
51
52
53 class Session(Thread, Subject):
54     
55     def __init__(self):
56         Thread.__init__(self, name='session')
57         Subject.__init__(self)
58         self._client_capabilities = CAPABILITIES
59         self._server_capabilities = None # yet
60         self._id = None # session-id
61         self._q = Queue()
62         self._connected = False # to be set/cleared by subclass implementation
63     
64     def _post_connect(self):
65         from ncclient.content.builders import HelloBuilder
66         self.send(HelloBuilder.build(self._client_capabilities))
67         error = None
68         init_event = Event()
69         def ok_cb(id, capabilities):
70             self._id, self._capabilities = id, Capabilities(capabilities)
71             init_event.set()
72         def err_cb(err):
73             error = err
74             init_event.set()
75         listener = HelloListener(ok_cb, err_cb)
76         self.add_listener(listener)
77         # start the subclass' main loop
78         self.start()        
79         # we expect server's hello message
80         init_event.wait()
81         # received hello message or an error happened
82         self.remove_listener(listener)
83         if error:
84             raise error
85         logger.debug('initialized:session-id:%s' % self._id)
86     
87     def send(self, message):
88         logger.debug('queueing:%s' % message)
89         self._q.put(message)
90     
91     def connect(self):
92         raise NotImplementedError
93
94     def run(self):
95         raise NotImplementedError
96         
97     def capabilities(self, whose='client'):
98         if whose == 'client':
99             return self._client_capabilities
100         elif whose == 'server':
101             return self._server_capabilities
102     
103     ### Properties
104     
105     @property
106     def client_capabilities(self):
107         return self._client_capabilities
108     
109     @property
110     def server_capabilities(self):
111         return self._server_capabilities
112     
113     @property
114     def connected(self):
115         return self._connected
116     
117     @property
118     def id(self):
119         return self._id
120
121
122 class HelloListener:
123     
124     def __init__(self, init_cb, error_cb):
125         self._init_cb, self._error_cb = init_cb, error_cb
126     
127     def __str__(self):
128         return 'HelloListener'
129     
130     ### Events
131     
132     def received(self, raw):
133         logger.debug(raw)
134         from ncclient.content.parsers import HelloParser
135         try:
136             id, capabilities = HelloParser.parse(raw)
137         except Exception as e:
138             self._error_cb(e)
139         else:
140             self._init_cb(id, capabilities)
141     
142     def error(self, err):
143         self._error_cb(err)
144
145
146 class DebugListener:
147     
148     def __str__(self):
149         return 'DebugListener'
150     
151     def received(self, raw):
152         logger.info('DebugListener:[received]:%s' % raw)
153     
154     def error(self, err):
155         logger.info('DebugListener:[error]:%s' % err)