nns.tcl

Go to the documentation of this file.
00001 /*  -*- tcl -*-*/
00002 /*  ### ### ### ######### ######### #########*/
00003 /*  Name Service - Client side access*/
00004 
00005 /*  ### ### ### ######### ######### #########*/
00006 /*  Requirements*/
00007 
00008 package require Tcl 8.4
00009 package require comm             ; /*  Generic message transport*/
00010 package require interp           ; /*  Interpreter helpers.*/
00011 package require logger           ; /*  Tracing internal activity*/
00012 package require nameserv::common ; /*  Common/shared utilities*/
00013 package require snit             ; /*  OO support, for streaming search class*/
00014 package require uevent           ; /*  Generate events for connection-loss*/
00015 
00016 namespace ::nameserv {}
00017 
00018 /*  ### ### ### ######### ######### #########*/
00019 /*  API: Write, Read, Search*/
00020 
00021 ret  ::nameserv::bind (type name , type data) {
00022     # Registers this application at the configured name service under
00023     # the specified name, and provides a value.
00024     #
00025     # Note: The application is allowed register multiple names.
00026     #
00027     # Note: A registered name is automatically removed by the server
00028     #       when the connection to it collapses.
00029 
00030     DO Bind $name $data
00031     return
00032 }
00033 
00034 ret  ::nameserv::release () {
00035     # Releases all names the application is registered under
00036     # at the configured name service.
00037 
00038     DO Release
00039     return
00040 }
00041 
00042 ret  ::nameserv::search (type args) {
00043     # Searches the configured name service for applications whose name
00044     # matches the given pattern. Returns a dictionary mapping from the
00045     # names to the data they provided at 'bind' time.
00046 
00047     # In continuous and async modes it returns an object whose
00048     # contents reflect the current set of matching entries.
00049 
00050     switch -exact [llength $args] {
00051     0 {
00052         set continuous 0
00053         set pattern    *
00054     }
00055     1 {
00056         set opt [lindex $args 0]
00057         if {$opt eq "-continuous"} {
00058         set oneshot    0
00059         set continuous 1
00060         set pattern    *
00061         } elseif {$opt eq "-async"} {
00062         set oneshot    1
00063         set continuous 1
00064         set pattern    *
00065         } else {
00066         set continuous 0
00067         set pattern    $opt
00068         }
00069     }
00070     2 {
00071         set opt [lindex $args 0]
00072         if {$opt eq "-continuous"} {
00073         set oneshot    0
00074         set continuous 1
00075         set pattern    [lindex $args 1]
00076         } elseif {$opt eq "-async"} {
00077         set oneshot    1
00078         set continuous 1
00079         set pattern    [lindex $args 1]
00080         } else {
00081         return -code error "wrong\#args: Expected ?-continuous|-async? ?pattern?"
00082         }
00083     }
00084     default {
00085         return -code error "wrong\#args: Expected ?-continuous|-async? ?pattern?"
00086     }
00087     }
00088 
00089     if {$continuous} {
00090     variable search
00091     # This client uses the receiver object as tag for the search
00092     # in the service. This is easily unique, and makes dispatch of
00093     # incoming results later easy too.
00094 
00095     set receiver [receiver %AUTO% $oneshot]
00096     ASYNC Search/Continuous/Start $receiver $pattern
00097 
00098     set search($receiver) .
00099     return $receiver
00100     } else {
00101     return [DO Search $pattern]
00102     }
00103 }
00104 
00105 ret  ::nameserv::protocol () {
00106     return 1
00107 }
00108 
00109 ret  ::nameserv::server_protocol () {
00110     return [DO ProtocolVersion]
00111 }
00112 
00113 ret  ::nameserv::server_features () {
00114     return [DO ProtocolFeatures]
00115 }
00116 
00117 /*  ### ### ### ######### ######### #########*/
00118 /*  INT: Communication setup / teardown / use*/
00119 
00120 ret  ::nameserv::DO (type args) {
00121     variable sid
00122     log::debug [linsert $args end @ $sid]
00123 
00124     if {[catch {
00125     [SERV] send $sid $args
00126     #eval [linsert $args 0 [SERV] send $sid] ;# $args
00127     } msg]} {
00128     if {[string match "*refused*" $msg]} {
00129         return -code error "No name server present @ $sid"
00130     } else {
00131         return -code error $msg
00132     }
00133     }
00134     # Result of the call
00135     return $msg
00136 }
00137 
00138 ret  ::nameserv::ASYNC (type args) {
00139     variable sid
00140     log::debug [linsert $args end @ $sid]
00141 
00142     if {[catch {
00143     [SERV] send -async $sid $args
00144     #eval [linsert $args 0 [SERV] send $sid] ;# $args
00145     } msg]} {
00146     if {[string match "*refused*" $msg]} {
00147         return -code error "No name server present @ $sid"
00148     } else {
00149         return -code error $msg
00150     }
00151     }
00152     # No result to return
00153     return
00154 }
00155 
00156 ret  ::nameserv::SERV () {
00157     variable comm
00158     variable sid
00159     variable host
00160     variable port
00161     if {$comm ne ""} {return $comm}
00162 
00163     # NOTE
00164     # -local 1 means that clients can only talk to a local
00165     #          name service. Might make sense to auto-force
00166     #          -local 0 for host ne "localhost".
00167 
00168     set     interp [interp::createEmpty]
00169     foreach msg {
00170     Search/Continuous/Change
00171     } {
00172     interp alias $interp $msg {} ::nameserv::$msg
00173     }
00174 
00175     set sid  [list $port $host]
00176     set comm [comm::comm new ::nameserv::CSERV \
00177           -interp $interp \
00178           -local  1 \
00179           -listen 1]
00180 
00181     $comm hook lost ::nameserv::LOST
00182 
00183     log::debug [list SERV @ $sid : $comm]
00184     return $comm
00185 }
00186 
00187 ret  ::nameserv::LOST (type args) {
00188     upvar 1 id id chan chan reason reason
00189     variable comm
00190     variable sid
00191     variable search
00192 
00193     log::debug [list LOST @ $sid - $reason]
00194 
00195     $comm destroy
00196 
00197     set comm {}
00198     set sid  {}
00199 
00200     # Notify async/cont search of the loss.
00201     foreach r [array names search] {
00202     $r DATA stop
00203     unset search($r)
00204     }
00205 
00206     uevent::generate nameserv lost-connection [list reason $reason]
00207     return
00208 }
00209 
00210 /*  ### ### ### ######### ######### #########*/
00211 /*  Initialization - System state*/
00212 
00213 namespace ::nameserv {
00214     /*  Object command of the communication channel to the server.*/
00215     /*  If present re-configuration is not possible. Also the comm*/
00216     /*  id of the server.*/
00217 
00218     variable comm {}
00219     variable sid  {}
00220 
00221     /*  Table of active async/cont searches*/
00222 
00223     variable search ; array  search =  {}
00224 }
00225 
00226 /*  ### ### ### ######### ######### #########*/
00227 /*  API: Configuration management (host, port)*/
00228 
00229 ret  ::nameserv::cget (type option) {
00230     return [configure $option]
00231 }
00232 
00233 ret  ::nameserv::configure (type args) {
00234     variable host
00235     variable port
00236 
00237     if {![llength $args]} {
00238     return [list -host $host -port $port]
00239     }
00240     if {[llength $args] == 1} {
00241     # cget
00242     set opt [lindex $args 0]
00243     switch -exact -- $opt {
00244         -host { return $host }
00245         -port { return $port }
00246         default {
00247         return -code error "bad option \"$opt\", expected -host, or -port"
00248         }
00249     }
00250     }
00251 
00252     if {$comm ne ""} {
00253     return -code error "Unable to configure an active connection"
00254     }
00255 
00256     # Note: Should -port/-host be made configurable after
00257     # communication has started it will be necessary to provide code
00258     # which retracts everything from the old server and re-initializes
00259     # the new one.
00260 
00261     while {[llength $args]} {
00262     set opt [lindex $args 0]
00263     switch -exact -- $opt {
00264         -host {
00265         if {[llength $args] % 2 == 1} {
00266             return -code error "value for \"$opt\" is missing"
00267         }
00268         set host [lindex $args 1]
00269         set args [lrange $args 2 end]
00270         }
00271         -port {
00272         if {[llength $args] % 2 == 1} {
00273             return -code error "value for \"$opt\" is missing"
00274         }
00275         set port [lindex $args 1]
00276         # Todo: Check non-zero unsigned short integer
00277         set args [lrange $args 2 end]
00278         }
00279         default {
00280         return -code error "bad option \"$opt\", expected -host, or -port"
00281         }
00282     }
00283     }
00284     return
00285 }
00286 
00287 /*  ### ### ### ######### ######### #########*/
00288 /*  Receiver for continuous and async searches*/
00289 
00290 ret  ::nameserv::Search/Continuous/Change (type tag , type type , type response) {
00291 
00292     # Ignore messages for searches which were canceled already.
00293     #
00294     # Due to the async nature of the messages for cont/async search
00295     # the client may have canceled the receiver object already, sent
00296     # the stop message already, but still has to process search
00297     # results which were already in flight. We ignore them.
00298 
00299     if {![llength [info commands $tag]]} return
00300 
00301     # This client uses the receiver object as tag, dispatch the
00302     # received notification to it.
00303 
00304     $tag DATA $type $response
00305     return
00306 }
00307 
00308 snit::type ::nameserv::receiver {
00309 
00310     option -command -default {}
00311 
00312     constructor {{once 0}} {
00313      singleshot =  $once
00314     return
00315     }
00316 
00317     destructor {
00318     if {$singleshot} return
00319     ASYNC Search/Continuous/Stop $self
00320     Callback stop {}
00321     return
00322     }
00323 
00324     ret  get (type k) {
00325     if {![info exists current($k)]} {return -code error "Unknown key \"$k\""}
00326     return $current($k)
00327     }
00328 
00329     ret  names () {
00330     return [array names current]
00331     }
00332 
00333     ret  size () {
00334     return [array size current]
00335     }
00336 
00337     ret  getall (optional pattern =*) {
00338     return [array get current $pattern]
00339     }
00340 
00341     ret  filled () {
00342     return $filled
00343     }
00344 
00345     ret  {DATA stop} () {
00346     if {$filled && $singleshot} return
00347     set singleshot 1 ; # Prevent 'stop' again during destruction.
00348     Callback stop {}
00349     return
00350     }
00351 
00352     ret  {DATA add} (type response) {
00353     set filled 1
00354     if {$singleshot} {
00355         ASYNC Search/Continuous/Stop $self
00356     }
00357     array set current $response
00358     Callback add $response
00359     if {$singleshot} {
00360         Callback stop {}
00361     }
00362     return
00363     }
00364 
00365     ret  {DATA remove} (type response) {
00366     set filled 1
00367     foreach {k v} $response {
00368         unset -nocomplain current($k)
00369     }
00370     Callback remove $response
00371     return
00372     }
00373 
00374     ret  Callback (type type , type response) {
00375     upvar 1 options options
00376     if {$options(-command) eq ""} return
00377     # Defer execution to event loop
00378     after 0 [linsert $options(-command) end $type $response]
00379     return
00380     }
00381 
00382     variable singleshot 0
00383     variable current -array {}
00384     variable filled 0
00385 }
00386 
00387 /*  ### ### ### ######### ######### #########*/
00388 /*  Initialization - Tracing, Configuration*/
00389 
00390 logger::initNamespace ::nameserv
00391 namespace        ::nameserv {
00392     /*  Host and port to connect to, to get access to the nameservice.*/
00393 
00394     variable host localhost
00395     variable port [nameserv::common::port]
00396 }
00397 
00398 /*  ### ### ### ######### ######### #########*/
00399 /*  Ready*/
00400 
00401 package provide nameserv 0.3
00402 
00403 /** 
00404  * ### ### ### ######### ######### #########
00405 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1