abcl.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP
 (HTM) git clone git://bitreich.org/clic/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/clic/
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Tags
 (DIR) README
 (DIR) LICENSE
       ---
       abcl.lisp (20212B)
       ---
            1 ;;;; New ABCL networking support (replacement to old armedbear.lisp)
            2 ;;;; Author: Chun Tian (binghe)
            3 
            4 ;;;; See LICENSE for licensing information.
            5 
            6 (in-package :usocket)
            7 
            8 ;;; Java Classes ($*...)
            9 (defvar $*boolean (jclass "boolean"))
           10 (defvar $*byte (jclass "byte"))
           11 (defvar $*byte[] (jclass "[B"))
           12 (defvar $*int (jclass "int"))
           13 (defvar $*long (jclass "long"))
           14 (defvar $*|Byte| (jclass "java.lang.Byte"))
           15 (defvar $*DatagramChannel (jclass "java.nio.channels.DatagramChannel"))
           16 (defvar $*DatagramPacket (jclass "java.net.DatagramPacket"))
           17 (defvar $*DatagramSocket (jclass "java.net.DatagramSocket"))
           18 (defvar $*Inet4Address (jclass "java.net.Inet4Address"))
           19 (defvar $*InetAddress (jclass "java.net.InetAddress"))
           20 (defvar $*InetSocketAddress (jclass "java.net.InetSocketAddress"))
           21 (defvar $*Iterator (jclass "java.util.Iterator"))
           22 (defvar $*SelectableChannel (jclass "java.nio.channels.SelectableChannel"))
           23 (defvar $*SelectionKey (jclass "java.nio.channels.SelectionKey"))
           24 (defvar $*Selector (jclass "java.nio.channels.Selector"))
           25 (defvar $*ServerSocket (jclass "java.net.ServerSocket"))
           26 (defvar $*ServerSocketChannel (jclass "java.nio.channels.ServerSocketChannel"))
           27 (defvar $*Set (jclass "java.util.Set"))
           28 (defvar $*Socket (jclass "java.net.Socket"))
           29 (defvar $*SocketAddress (jclass "java.net.SocketAddress"))
           30 (defvar $*SocketChannel (jclass "java.nio.channels.SocketChannel"))
           31 (defvar $*String (jclass "java.lang.String"))
           32 
           33 ;;; Java Constructor ($%.../n)
           34 (defvar $%Byte/0 (jconstructor $*|Byte| $*byte))
           35 (defvar $%DatagramPacket/3 (jconstructor $*DatagramPacket $*byte[] $*int $*int))
           36 (defvar $%DatagramPacket/5 (jconstructor $*DatagramPacket $*byte[] $*int $*int $*InetAddress $*int))
           37 (defvar $%DatagramSocket/0 (jconstructor $*DatagramSocket))
           38 (defvar $%DatagramSocket/1 (jconstructor $*DatagramSocket $*int))
           39 (defvar $%DatagramSocket/2 (jconstructor $*DatagramSocket $*int $*InetAddress))
           40 (defvar $%InetSocketAddress/1 (jconstructor $*InetSocketAddress $*int))
           41 (defvar $%InetSocketAddress/2 (jconstructor $*InetSocketAddress $*InetAddress $*int))
           42 (defvar $%ServerSocket/0 (jconstructor $*ServerSocket))
           43 (defvar $%ServerSocket/1 (jconstructor $*ServerSocket $*int))
           44 (defvar $%ServerSocket/2 (jconstructor $*ServerSocket $*int $*int))
           45 (defvar $%ServerSocket/3 (jconstructor $*ServerSocket $*int $*int $*InetAddress))
           46 (defvar $%Socket/0 (jconstructor $*Socket))
           47 (defvar $%Socket/2 (jconstructor $*Socket $*InetAddress $*int))
           48 (defvar $%Socket/4 (jconstructor $*Socket $*InetAddress $*int $*InetAddress $*int))
           49 
           50 ;;; Java Methods ($@...[/Class]/n)
           51 (defvar $@accept/0 (jmethod $*ServerSocket "accept"))
           52 (defvar $@bind/DatagramSocket/1 (jmethod $*DatagramSocket "bind" $*SocketAddress))
           53 (defvar $@bind/ServerSocket/1 (jmethod $*ServerSocket "bind" $*SocketAddress))
           54 (defvar $@bind/ServerSocket/2 (jmethod $*ServerSocket "bind" $*SocketAddress $*int))
           55 (defvar $@bind/Socket/1 (jmethod $*Socket "bind" $*SocketAddress))
           56 (defvar $@byteValue/0 (jmethod $*|Byte| "byteValue"))
           57 (defvar $@channel/0 (jmethod $*SelectionKey "channel"))
           58 (defvar $@close/DatagramSocket/0 (jmethod $*DatagramSocket "close"))
           59 (defvar $@close/Selector/0 (jmethod $*Selector "close"))
           60 (defvar $@close/ServerSocket/0 (jmethod $*ServerSocket "close"))
           61 (defvar $@close/Socket/0 (jmethod $*Socket "close"))
           62 (defvar $@shutdownInput/Socket/0 (jmethod $*Socket "shutdownInput"))
           63 (defvar $@shutdownOutput/Socket/0 (jmethod $*Socket "shutdownOutput"))
           64 (defvar $@configureBlocking/1 (jmethod $*SelectableChannel "configureBlocking" $*boolean))
           65 (defvar $@connect/DatagramChannel/1 (jmethod $*DatagramChannel "connect" $*SocketAddress))
           66 (defvar $@connect/Socket/1 (jmethod $*Socket "connect" $*SocketAddress))
           67 (defvar $@connect/Socket/2 (jmethod $*Socket "connect" $*SocketAddress $*int))
           68 (defvar $@connect/SocketChannel/1 (jmethod $*SocketChannel "connect" $*SocketAddress))
           69 (defvar $@getAddress/0 (jmethod $*InetAddress "getAddress"))
           70 (defvar $@getAllByName/1 (jmethod $*InetAddress "getAllByName" $*String))
           71 (defvar $@getByName/1 (jmethod $*InetAddress "getByName" $*String))
           72 (defvar $@getChannel/DatagramSocket/0 (jmethod $*DatagramSocket "getChannel"))
           73 (defvar $@getChannel/ServerSocket/0 (jmethod $*ServerSocket "getChannel"))
           74 (defvar $@getChannel/Socket/0 (jmethod $*Socket "getChannel"))
           75 (defvar $@getAddress/DatagramPacket/0 (jmethod $*DatagramPacket "getAddress"))
           76 (defvar $@getHostName/0 (jmethod $*InetAddress "getHostName"))
           77 (defvar $@getInetAddress/DatagramSocket/0 (jmethod $*DatagramSocket "getInetAddress"))
           78 (defvar $@getInetAddress/ServerSocket/0 (jmethod $*ServerSocket "getInetAddress"))
           79 (defvar $@getInetAddress/Socket/0 (jmethod $*Socket "getInetAddress"))
           80 (defvar $@getLength/DatagramPacket/0 (jmethod $*DatagramPacket "getLength"))
           81 (defvar $@getLocalAddress/DatagramSocket/0 (jmethod $*DatagramSocket "getLocalAddress"))
           82 (defvar $@getLocalAddress/Socket/0 (jmethod $*Socket "getLocalAddress"))
           83 (defvar $@getLocalPort/DatagramSocket/0 (jmethod $*DatagramSocket "getLocalPort"))
           84 (defvar $@getLocalPort/ServerSocket/0 (jmethod $*ServerSocket "getLocalPort"))
           85 (defvar $@getLocalPort/Socket/0 (jmethod $*Socket "getLocalPort"))
           86 (defvar $@getOffset/DatagramPacket/0 (jmethod $*DatagramPacket "getOffset"))
           87 (defvar $@getPort/DatagramPacket/0 (jmethod $*DatagramPacket "getPort"))
           88 (defvar $@getPort/DatagramSocket/0 (jmethod $*DatagramSocket "getPort"))
           89 (defvar $@getPort/Socket/0 (jmethod $*Socket "getPort"))
           90 (defvar $@hasNext/0 (jmethod $*Iterator "hasNext"))
           91 (defvar $@iterator/0 (jmethod $*Set "iterator"))
           92 (defvar $@next/0 (jmethod $*Iterator "next"))
           93 (defvar $@open/DatagramChannel/0 (jmethod $*DatagramChannel "open"))
           94 (defvar $@open/Selector/0 (jmethod $*Selector "open"))
           95 (defvar $@open/ServerSocketChannel/0 (jmethod $*ServerSocketChannel "open"))
           96 (defvar $@open/SocketChannel/0 (jmethod $*SocketChannel "open"))
           97 (defvar $@receive/1 (jmethod $*DatagramSocket "receive" $*DatagramPacket))
           98 (defvar $@register/2 (jmethod $*SelectableChannel "register" $*Selector $*int))
           99 (defvar $@select/0 (jmethod $*Selector "select"))
          100 (defvar $@select/1 (jmethod $*Selector "select" $*long))
          101 (defvar $@selectedKeys/0 (jmethod $*Selector "selectedKeys"))
          102 (defvar $@send/1 (jmethod $*DatagramSocket "send" $*DatagramPacket))
          103 (defvar $@setReuseAddress/1 (jmethod $*ServerSocket "setReuseAddress" $*boolean))
          104 (defvar $@setSoTimeout/DatagramSocket/1 (jmethod $*DatagramSocket "setSoTimeout" $*int))
          105 (defvar $@setSoTimeout/Socket/1 (jmethod $*Socket "setSoTimeout" $*int))
          106 (defvar $@setTcpNoDelay/1 (jmethod $*Socket "setTcpNoDelay" $*boolean))
          107 (defvar $@socket/DatagramChannel/0 (jmethod $*DatagramChannel "socket"))
          108 (defvar $@socket/ServerSocketChannel/0 (jmethod $*ServerSocketChannel "socket"))
          109 (defvar $@socket/SocketChannel/0 (jmethod $*SocketChannel "socket"))
          110 (defvar $@validOps/0 (jmethod $*SelectableChannel "validOps"))
          111 
          112 ;;; Java Field Variables ($+...)
          113 (defvar $+op-accept (jfield $*SelectionKey "OP_ACCEPT"))
          114 (defvar $+op-connect (jfield $*SelectionKey "OP_CONNECT"))
          115 (defvar $+op-read (jfield $*SelectionKey "OP_READ"))
          116 (defvar $+op-write (jfield $*SelectionKey "OP_WRITE"))
          117 
          118 
          119 ;;; Wrapper functions (return-type: java-object)
          120 (defun %get-address (address)
          121   (jcall $@getAddress/0 address))
          122 (defun %get-all-by-name (string) ; return a simple vector
          123   (jstatic $@getAllByName/1 $*InetAddress string))
          124 (defun %get-by-name (string)
          125   (jstatic $@getByName/1 $*InetAddress string))
          126 
          127 (defun host-to-inet4 (host)
          128   "USOCKET host formats to Java Inet4Address, used internally."
          129   (%get-by-name (host-to-hostname host)))
          130 
          131 ;;; HANDLE-CONTITION
          132 
          133 (defparameter +abcl-error-map+
          134   `(("java.net.BindException" . operation-not-permitted-error)
          135     ("java.net.ConnectException" . connection-refused-error)
          136     ("java.net.NoRouteToHostException" . network-unreachable-error) ; untested
          137     ("java.net.PortUnreachableException" . protocol-not-supported-error) ; untested
          138     ("java.net.ProtocolException" . protocol-not-supported-error) ; untested
          139     ("java.net.SocketException" . socket-type-not-supported-error) ; untested
          140     ("java.net.SocketTimeoutException" . timeout-error)))
          141 
          142 (defparameter +abcl-nameserver-error-map+
          143   `(("java.net.UnknownHostException" . ns-host-not-found-error)))
          144 
          145 (defun handle-condition (condition &optional (socket nil) (host-or-ip nil))
          146   (typecase condition
          147     (java-exception
          148      (let ((java-cause (java-exception-cause condition)))
          149        (let* ((usock-error (cdr (assoc (jclass-of java-cause) +abcl-error-map+
          150                                        :test #'string=)))
          151               (usock-error (if (functionp usock-error)
          152                                (funcall usock-error condition)
          153                                usock-error))
          154               (nameserver-error (cdr (assoc (jclass-of java-cause) +abcl-nameserver-error-map+
          155                                             :test #'string=))))
          156          (if nameserver-error
          157              (error nameserver-error :socket socket :host-or-ip host-or-ip)
          158              (when usock-error
          159                (error usock-error :socket socket))))))))
          160 
          161 ;;; GET-HOSTS-BY-NAME
          162 
          163 (defun get-address (address)
          164   (when address
          165     (let* ((array (%get-address address))
          166            (length (jarray-length array)))
          167       (labels ((jbyte (n)
          168                  (let ((byte (jarray-ref array n)))
          169                    (if (minusp byte) (+ 256 byte) byte))))
          170         (cond  
          171           ((= 4 length)
          172            (vector (jbyte 0) (jbyte 1) (jbyte 2) (jbyte 3)))
          173           ((= 16 length)
          174            (vector (jbyte 0) (jbyte 1) (jbyte 2) (jbyte 3) 
          175                    (jbyte 4) (jbyte 5) (jbyte 6) (jbyte 7)
          176                    (jbyte 8) (jbyte 9) (jbyte 10) (jbyte 11)
          177                    (jbyte 12) (jbyte 13) (jbyte 14) (jbyte 15)))
          178           (t nil)))))) ; neither a IPv4 nor IPv6 address?!
          179 
          180 (defun get-hosts-by-name (name)
          181   (with-mapped-conditions (nil name)
          182     (map 'list #'get-address (%get-all-by-name name))))
          183 
          184 ;;; GET-HOST-BY-ADDRESS
          185 
          186 (defun get-host-by-address (host)
          187   (let ((inet4 (host-to-inet4 host)))
          188     (with-mapped-conditions (nil host)
          189       (jcall $@getHostName/0 inet4))))
          190 
          191 ;;; SOCKET-CONNECT
          192 
          193 (defun socket-connect (host port &key (protocol :stream) (element-type 'character)
          194                        timeout deadline (nodelay t nodelay-supplied-p)
          195                        local-host local-port)
          196   (when deadline (unsupported 'deadline 'socket-connect))
          197   (let (socket stream usocket)
          198     (ecase protocol
          199       (:stream ; TCP
          200        (let ((channel (jstatic $@open/SocketChannel/0 $*SocketChannel))
          201              (address (jnew $%InetSocketAddress/2 (host-to-inet4 host) port)))
          202          (setq socket (jcall $@socket/SocketChannel/0 channel))
          203          ;; bind to local address if needed
          204          (when (or local-host local-port)
          205            (let ((local-address (jnew $%InetSocketAddress/2 (host-to-inet4 local-host) (or local-port 0))))
          206              (with-mapped-conditions (nil host)
          207                (jcall $@bind/Socket/1 socket local-address))))
          208          ;; connect to dest address
          209          (with-mapped-conditions (nil host)
          210            (jcall $@connect/SocketChannel/1 channel address))
          211          (setq stream (ext:get-socket-stream socket :element-type element-type)
          212                usocket (make-stream-socket :stream stream :socket socket))
          213          (when nodelay-supplied-p
          214            (jcall $@setTcpNoDelay/1 socket (if nodelay ;; both t and :if-supported mean java:+true+
          215                                            java:+true+ java:+false+)))
          216          (when timeout
          217            (jcall $@setSoTimeout/Socket/1 socket (truncate (* 1000 timeout))))))
          218       (:datagram ; UDP
          219        (let ((channel (jstatic $@open/DatagramChannel/0 $*DatagramChannel)))
          220          (setq socket (jcall $@socket/DatagramChannel/0 channel))
          221          ;; bind to local address if needed
          222          (when (or local-host local-port)
          223            (let ((local-address (jnew $%InetSocketAddress/2 (host-to-inet4 local-host) (or local-port 0))))
          224              (with-mapped-conditions (nil local-host)
          225                (jcall $@bind/DatagramSocket/1 socket local-address))))
          226          ;; connect to dest address if needed
          227          (when (and host port)
          228            (let ((address (jnew $%InetSocketAddress/2 (host-to-inet4 host) port)))
          229              (with-mapped-conditions (nil host)
          230                (jcall $@connect/DatagramChannel/1 channel address))))
          231          (setq usocket (make-datagram-socket socket :connected-p (if (and host port) t nil)))
          232          (when timeout
          233            (jcall $@setSoTimeout/DatagramSocket/1 socket (truncate (* 1000 timeout)))))))
          234     usocket))
          235 
          236 ;;; SOCKET-LISTEN
          237 
          238 (defun socket-listen (host port &key reuseaddress
          239                       (reuse-address nil reuse-address-supplied-p)
          240                       (backlog 5 backlog-supplied-p)
          241                       (element-type 'character))
          242   (declare (type boolean reuse-address))
          243   (let* ((reuseaddress (if reuse-address-supplied-p reuse-address reuseaddress))
          244          (channel (jstatic $@open/ServerSocketChannel/0 $*ServerSocketChannel))
          245          (socket (jcall $@socket/ServerSocketChannel/0 channel))
          246          (endpoint (jnew $%InetSocketAddress/2 (host-to-inet4 host) (or port 0))))
          247     (jcall $@setReuseAddress/1 socket (if reuseaddress java:+true+ java:+false+))
          248     (with-mapped-conditions (socket host)
          249       (if backlog-supplied-p
          250           (jcall $@bind/ServerSocket/2 socket endpoint backlog)
          251           (jcall $@bind/ServerSocket/1 socket endpoint)))
          252     (make-stream-server-socket socket :element-type element-type)))
          253 
          254 ;;; SOCKET-ACCEPT
          255 
          256 (defmethod socket-accept ((usocket stream-server-usocket) 
          257                           &key (element-type 'character element-type-p))
          258   (with-mapped-conditions (usocket)
          259     (let* ((client-socket (jcall $@accept/0 (socket usocket)))
          260            (element-type (if element-type-p 
          261                              element-type
          262                              (element-type usocket)))
          263            (stream (ext:get-socket-stream client-socket :element-type element-type)))
          264       (make-stream-socket :stream stream :socket client-socket))))
          265 
          266 ;;; SOCKET-CLOSE
          267 
          268 (defmethod socket-close ((usocket stream-server-usocket))
          269   (with-mapped-conditions (usocket)
          270     (jcall $@close/ServerSocket/0 (socket usocket))))
          271 
          272 (defmethod socket-close ((usocket stream-usocket))
          273   (with-mapped-conditions (usocket)
          274     (close (socket-stream usocket))
          275     (jcall $@close/Socket/0 (socket usocket))))
          276 
          277 (defmethod socket-close ((usocket datagram-usocket))
          278   (with-mapped-conditions (usocket)
          279     (jcall $@close/DatagramSocket/0 (socket usocket))))
          280 
          281 (defmethod socket-shutdown ((usocket stream-usocket) direction)
          282   (with-mapped-conditions (usocket)
          283     (ecase direction
          284       (:input
          285        (jcall $@shutdownInput/Socket/0 (socket usocket)))
          286       (:output
          287        (jcall $@shutdownOutput/Socket/0 (socket usocket))))))
          288 
          289 ;;; GET-LOCAL/PEER-NAME/ADDRESS/PORT
          290 
          291 (defmethod get-local-name ((usocket usocket))
          292   (values (get-local-address usocket)
          293           (get-local-port usocket)))
          294 
          295 (defmethod get-peer-name ((usocket usocket))
          296   (values (get-peer-address usocket)
          297           (get-peer-port usocket)))
          298 
          299 (defmethod get-local-address ((usocket stream-usocket))
          300   (get-address (jcall $@getLocalAddress/Socket/0 (socket usocket))))
          301 
          302 (defmethod get-local-address ((usocket stream-server-usocket))
          303   (get-address (jcall $@getInetAddress/ServerSocket/0 (socket usocket))))
          304 
          305 (defmethod get-local-address ((usocket datagram-usocket))
          306   (get-address (jcall $@getLocalAddress/DatagramSocket/0 (socket usocket))))
          307 
          308 (defmethod get-peer-address ((usocket stream-usocket))
          309   (get-address (jcall $@getInetAddress/Socket/0 (socket usocket))))
          310 
          311 (defmethod get-peer-address ((usocket datagram-usocket))
          312   (get-address (jcall $@getInetAddress/DatagramSocket/0 (socket usocket))))
          313 
          314 (defmethod get-local-port ((usocket stream-usocket))
          315   (jcall $@getLocalPort/Socket/0 (socket usocket)))
          316 
          317 (defmethod get-local-port ((usocket stream-server-usocket))
          318   (jcall $@getLocalPort/ServerSocket/0 (socket usocket)))
          319 
          320 (defmethod get-local-port ((usocket datagram-usocket))
          321   (jcall $@getLocalPort/DatagramSocket/0 (socket usocket)))
          322 
          323 (defmethod get-peer-port ((usocket stream-usocket))
          324   (jcall $@getPort/Socket/0 (socket usocket)))
          325 
          326 (defmethod get-peer-port ((usocket datagram-usocket))
          327   (jcall $@getPort/DatagramSocket/0 (socket usocket)))
          328 
          329 ;;; SOCKET-SEND & SOCKET-RECEIVE
          330 
          331 (defun *->byte (data)
          332   (declare (type (unsigned-byte 8) data)) ; required by SOCKET-SEND
          333   (jnew $%Byte/0 (if (> data 127) (- data 256) data)))
          334 
          335 (defun byte->* (byte &optional (element-type '(unsigned-byte 8)))
          336   (let* ((ub8 (if (minusp byte) (+ 256 byte) byte)))
          337     (if (eq element-type 'character)
          338         (code-char ub8)
          339         ub8)))
          340 
          341 (defmethod socket-send ((usocket datagram-usocket) buffer size &key host port (offset 0))
          342   (let* ((socket (socket usocket))
          343          (byte-array (jnew-array $*byte size))
          344          (packet (if (and host port)
          345                      (jnew $%DatagramPacket/5 byte-array 0 size (host-to-inet4 host) port)
          346                      (jnew $%DatagramPacket/3 byte-array 0 size))))
          347     ;; prepare sending data
          348     (loop for i from offset below (+ size offset)
          349        do (setf (jarray-ref byte-array i) (*->byte (aref buffer i))))
          350     (with-mapped-conditions (usocket host)
          351       (jcall $@send/1 socket packet))))
          352 
          353 ;;; TODO: return-host and return-port cannot be get ...
          354 (defmethod socket-receive ((usocket datagram-usocket) buffer length
          355                            &key (element-type '(unsigned-byte 8)))
          356   (declare (values (simple-array (unsigned-byte 8) (*)) ; buffer
          357                    (integer 0)                          ; size
          358                    (unsigned-byte 32)                   ; host
          359                    (unsigned-byte 16)))                 ; port
          360   (let* ((socket (socket usocket))
          361          (real-length (or length +max-datagram-packet-size+))
          362          (byte-array (jnew-array $*byte real-length))
          363          (packet (jnew $%DatagramPacket/3 byte-array 0 real-length)))
          364     (with-mapped-conditions (usocket)
          365       (jcall $@receive/1 socket packet))
          366     (let* ((receive-length (jcall $@getLength/DatagramPacket/0 packet))
          367            (return-buffer (or buffer (make-array receive-length :element-type element-type))))
          368       (loop for i from 0 below receive-length
          369          do (setf (aref return-buffer i)
          370                   (byte->* (jarray-ref byte-array i) element-type)))
          371       (let ((return-host (if (connected-p usocket)
          372                              (get-peer-address usocket)
          373                              (get-address (jcall $@getAddress/DatagramPacket/0 packet))))
          374             (return-port (if (connected-p usocket)
          375                              (get-peer-port usocket)
          376                              (jcall $@getPort/DatagramPacket/0 packet))))
          377         (values return-buffer
          378                 receive-length
          379                 return-host
          380                 return-port)))))
          381 
          382 ;;; WAIT-FOR-INPUT
          383 
          384 (defun socket-channel-class (usocket)
          385   (cond ((stream-usocket-p usocket) $*SocketChannel)
          386         ((stream-server-usocket-p usocket) $*ServerSocketChannel)
          387         ((datagram-usocket-p usocket) $*DatagramChannel)))
          388 
          389 (defun get-socket-channel (usocket)
          390   (let ((method (cond ((stream-usocket-p usocket) $@getChannel/Socket/0)
          391                       ((stream-server-usocket-p usocket) $@getChannel/ServerSocket/0)
          392                       ((datagram-usocket-p usocket) $@getChannel/DatagramSocket/0))))
          393     (jcall method (socket usocket))))
          394 
          395 (defun wait-for-input-internal (wait-list &key timeout)
          396   (let* ((sockets (wait-list-waiters wait-list))
          397          (ops (logior $+op-read $+op-accept))
          398          (selector (jstatic $@open/Selector/0 $*Selector))
          399          (channels (mapcar #'get-socket-channel sockets)))
          400     (unwind-protect
          401          (with-mapped-conditions ()
          402            (dolist (channel channels)
          403              (jcall $@configureBlocking/1 channel java:+false+)
          404              (jcall $@register/2 channel selector (logand ops (jcall $@validOps/0 channel))))
          405            (let ((ready-count (if timeout
          406                                   (jcall $@select/1 selector (truncate (* timeout 1000)))
          407                                   (jcall $@select/0 selector))))
          408              (when (plusp ready-count)
          409                (let* ((keys (jcall $@selectedKeys/0 selector))
          410                       (iterator (jcall $@iterator/0 keys))
          411                       (%wait (wait-list-%wait wait-list)))
          412                  (loop while (jcall $@hasNext/0 iterator)
          413                        do (let* ((key (jcall $@next/0 iterator))
          414                                  (channel (jcall $@channel/0 key)))
          415                             (setf (state (gethash channel %wait)) :read)))))))
          416       (jcall $@close/Selector/0 selector)
          417       (dolist (channel channels)
          418         (jcall $@configureBlocking/1 channel java:+true+)))))
          419 
          420 ;;; WAIT-LIST
          421 
          422 ;;; NOTE from original worker (Erik):
          423 ;;; Note that even though Java has the concept of the Selector class, which
          424 ;;; remotely looks like a wait-list, it requires the sockets to be non-blocking.
          425 ;;; usocket however doesn't make any such guarantees and is therefore unable to
          426 ;;; use the concept outside of the waiting routine itself (blergh!).
          427 
          428 (defun %setup-wait-list (wl)
          429   (setf (wait-list-%wait wl)
          430         (make-hash-table :test #'equal :rehash-size 1.3d0)))
          431 
          432 (defun %add-waiter (wl w)
          433   (setf (gethash (get-socket-channel w) (wait-list-%wait wl)) w))
          434 
          435 (defun %remove-waiter (wl w)
          436   (remhash (get-socket-channel w) (wait-list-%wait wl)))