git-svn-id: http://ncclient.googlecode.com/svn/trunk@107 6dbcf712-26ac-11de-a2f3...
[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 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