1 # dc-link.tcl --
2 #
3 # Contains CLink and CLinkManager, basic building blocks of DC
4 # Service Discovery Service.
5 #
6 # Copyright (c) 2000-2002 The Regents of the University of California.
7 # All rights reserved.
8 #
9 # Redistribution and use in source and binary forms, with or without
10 # modification, are permitted provided that the following conditions are met:
11 #
12 # A. Redistributions of source code must retain the above copyright notice,
13 # this list of conditions and the following disclaimer.
14 # B. Redistributions in binary form must reproduce the above copyright notice,
15 # this list of conditions and the following disclaimer in the documentation
16 # and/or other materials provided with the distribution.
17 # C. Neither the names of the copyright holders nor the names of its
18 # contributors may be used to endorse or promote products derived from this
19 # software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
22 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
25 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32 Class CLinkManager
33 #-----------------------------------------------------------------------------
34 #
35 # Class CLinkManager
36 #
37 # Purpose:
38 # Maintains a list of links between self and remote processes.
39 # Members:
40 # m_socketServer - server socket of self
41 # m_lLink - a list of CLink objects
42 # m_lAttach - a list of class attached to this object so that
43 # they can receive notification whenever a new connection
44 # arrive. (Can't we use the Observable/Observer pattern?)
45 #
46 #-----------------------------------------------------------------------------
47
48
49 #-----------------------------------------------------------------------------
50 #
51 # CLinkManager constructor
52 #
53 # Input:
54 # iPort - port to listen to for connection request
55 #
56 # Description:
57 # Creates a socket and listen to client's request. Initialize members
58 # to empty.
59 #
60 #-----------------------------------------------------------------------------
61 CLinkManager public init { iPort } {
62 $self instvar m_socketServer
63 $self instvar m_lLink
64
65 set m_lLink ""
66
67 if {[catch {socket -server "$self NewConnection" $iPort} m_socketServer]} {
68 return -code error "LinkManager: Couldn't create server socket"
69 }
70 }
71
72
73 #-----------------------------------------------------------------------------
74 #
75 # CLinkManager destructor
76 #
77 # Input:
78 # none
79 #
80 # Description:
81 # close the server, and delete each links
82 #
83 #-----------------------------------------------------------------------------
84 CLinkManager public destroy { } {
85 $self instvar m_socketServer
86 $self instvar m_lLink
87
88 if {[catch {close $m_socketServer} err]} {
89 puts "CLinkManager: Unable to close socket."
90 }
91
92 if {$m_lLink != ""} {
93 foreach link $m_lLink {
94 delete $link
95 }
96 }
97 }
98
99 CLinkManager public Attach { objAttach } {
100 $self instvar m_lAttach
101
102 lappend m_lAttach $objAttach
103 }
104
105
106 #-----------------------------------------------------------------------------
107 #
108 # CLinkManager NewConnection
109 #
110 # Input:
111 # newSocket, inetAddr, iPort - socket, address and port number of
112 # the new connection.
113 #
114 # Description:
115 # This is the callback that is executed when a client connects to
116 # this server.
117 #
118 #-----------------------------------------------------------------------------
119 CLinkManager private NewConnection { newSocket inetAddr iPort } {
120 $self instvar m_lLink m_lAttach
121
122 # create the new link with the socket
123 set link [new CLink $newSocket $inetAddr $iPort]
124 foreach a $m_lAttach {
125 set result [$a NewConnection $link]
126 if {$result != 0} {
127 break
128 }
129 }
130
131 # stick it into a list so that we can cleanup afterwards
132 lappend m_lLink $link
133 }
134
135 #-----------------------------------------------------------------------------
136 #
137 # I don't think NewLink is called anywhere.. - weitsang
138 #
139 #-----------------------------------------------------------------------------
140 #CLinkManager instproc NewLink { inetRemoteAddr iRemotePort } {
141 # set sock [socket $inetRemoteAddr $iRemotePort]
142 #
143 # if { $sock <= 0 } {
144 # puts "CLinkManager: NewLink couldn't connect socket to host"
145 # return 0
146 # }
147 #
148 # set link [new CLink $sock $inetRemoteAddr $iRemotePort]
149 #
150 # return $link
151 #}
152
153 #-----------------------------------------------------------------------------
154 #
155 # CLinkManager GetSpec
156 #
157 # Description:
158 # Returns a textual information about the current socket. Return
159 # string starts with the local address and is followed by socket
160 # information.
161 #
162 #-----------------------------------------------------------------------------
163 CLinkManager instproc GetSpec {} {
164 $self instvar m_socketServer
165
166 if {[catch {fconfigure $m_socketServer -sockname} socketInfo]} {
167 return "[localaddr]: no socket info"
168 }
169 return "[localaddr] [lindex $socketInfo 2]"
170 }
171
172
173 #-----------------------------------------------------------------------------
174 #
175 # Class CLink
176 #
177 # CLink provides an abstraction for a connection between the client
178 # and a service.
179 #
180 # Members-
181 # m_socket - socket for this connection
182 # m_aMessageMap - an array of object/methods pair indexed by
183 # messages. When a message is received, the
184 # corresponding method is executed.
185 # m_bufferRead - buffer of messages for reading
186 #
187 #-----------------------------------------------------------------------------
188 Class CLink
189
190
191 #-----------------------------------------------------------------------------
192 #
193 # CLink constructor
194 #
195 # Input:
196 # socket - socket for this connection
197 #
198 #-----------------------------------------------------------------------------
199 CLink private init { sock } {
200 $self instvar m_socket
201 $self instvar m_aMessageMap
202 $self instvar m_bufferRead
203
204 set m_bufferRead ""
205
206 set m_socket $sock
207
208 # set the socket to be non-blocking
209 if {[catch {fconfigure $m_socket -blocking 0}]} {
210 puts "CLink: Unable to configure socket"
211 return
212 }
213
214 # handle the event when the socket has something to read
215 if {[catch {fileevent $m_socket readable "$self Receive"}]} {
216 puts "CLink: fileevent readable failed"
217 }
218 }
219
220
221 #-----------------------------------------------------------------------------
222 #
223 # CLink destructor
224 #
225 # Purpose:
226 # close the socket association with this link
227 #
228 #-----------------------------------------------------------------------------
229 CLink public destroy { } {
230 $self CloseLink
231 }
232
233
234 #-----------------------------------------------------------------------------
235 #
236 # CLink MapMessage
237 #
238 # Input:
239 # strMessage, obj, fnMethod
240 #
241 # Purpose:
242 # Add the method $fnMethod of object $obj to the list of callbacks to
243 # run when the message $strMessage is received.
244 #
245 #-----------------------------------------------------------------------------
246 CLink public MapMessage { strMessage obj fnMethod } {
247 $self instvar m_aMessageMap
248
249 # the the current list of
250 if { ![info exist m_aMessageMap($strMessage)] } {
251 set m_aMessageMap($strMessage) ""
252 }
253
254 # now add the new map in
255 append m_aMessageMap($strMessage) " " [concat $obj $fnMethod]
256 }
257
258
259 #-----------------------------------------------------------------------------
260 #
261 # CLink UnmapMessage
262 #
263 # Input:
264 # strMessage, obj, fnMethod
265 #
266 # Purpose:
267 # Remove the method $fnMethod of object $obj from the list of callbacks to
268 # run when the message $strMessage is received.
269 #
270 #-----------------------------------------------------------------------------
271 CLink public UnmapMessage { strMessage obj fnMethod } {
272 $self instvar m_aMessageMap
273
274 if { [info exist m_aMessageMap($strMessage)] } {
275 set map $m_aMessageMap($strMessage)
276 } else {
277 return
278 }
279
280 # now the delete map in the map
281 set newMap ""
282 foreach { objMap fnMethodMap } $map {
283 if { $objMap != $obj || $fnMethodMap != $fnMethod } {
284 lappend newMap $objMap $fnMethod
285 }
286 }
287
288 set m_aMessageMap($strMessage) $newMap
289 }
290
291
292 #-----------------------------------------------------------------------------
293 #
294 # CLink SocketRead
295 #
296 # Purpose:
297 # Fileevent readable callback for the connection to the service.
298 #
299 #-----------------------------------------------------------------------------
300 CLink private Receive { } {
301 $self instvar m_socket
302 $self instvar m_bufferRead
303
304 # check that the socket is ok
305 if { [catch {eof $m_socket} status] } {
306 puts "CLink: Unable to check socket's status, closing all links."
307 $self CloseLink
308 return
309 }
310 if {$status == 1} {
311 $self CloseLink
312 return
313 }
314
315 if {[catch {gets $m_socket line} n]} {
316 # If we get an exception reading from the socket,
317 # reset and give up
318 puts "CLink: Unable to read a line from socket"
319 return
320 }
321 while { $n > 0 } {
322 # check if the line is the end of message signal
323 if { $line == "__END_OF_MESSAGE" } {
324 # if so then process the buffer
325 $self ProcessMessage $m_bufferRead
326 set m_bufferRead ""
327 } else {
328 # else it's part of a message to add it to the read buffer
329 set m_bufferRead "$m_bufferRead \n$line"
330 }
331 # read the next line
332 if {[catch {gets $m_socket line} n]} {
333 # If we get an exception reading from the socket,
334 # reset and give up
335 puts "CLink: Unable to read a line from socket. Giving up."
336 set m_bufferRead ""
337 return
338 }
339 }
340 }
341
342 CLink private ProcessMessage { cmd } {
343 $self instvar m_aMessageMap
344
345 # get the first word of the cmd and use the message map to see who
346 # to send it to
347
348 set message [lindex $cmd 0]
349 set arguments [lrange $cmd 1 end]
350
351 # if the message isn't mapped then just return
352 if { ![info exists m_aMessageMap($message)] } {
353 return 0
354 }
355
356 set lInvoke $m_aMessageMap($message)
357 foreach { obj fnMethod } $lInvoke {
358 eval "$obj $fnMethod $self $arguments"
359 }
360 }
361
362 CLink public Send { message args } {
363 $self instvar m_socket
364
365 # for now just block on write
366 if {[catch {puts $m_socket "$message $args \n__END_OF_MESSAGE"} err]} {
367 puts "CLink: Unable to write to socket"
368 }
369 if {[catch {flush $m_socket} err]} {
370 puts "CLink: Unable to flush socket"
371 }
372 }
373
374
375 CLink public CloseLink { } {
376 $self instvar m_socket
377
378 if {[catch {close $m_socket} err]} {
379 puts "CLink: Unable to close socket"
380 }
381
382 # notify the user that we've closed the socket
383 $self ProcessMessage {CLOSE_LINK {}}
384 }
385
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.