001: /*
002: * ServerSocketChannel.java
003: *
004: * Implements a server side socket channel for the Jacl
005: * interpreter.
006: */
007: package tcl.lang;
008:
009: import java.io.*;
010: import java.net.*;
011: import java.util.Hashtable;
012:
013: /**
014: * The ServerSocketChannel class implements a channel object for
015: * ServerSocket connections, created using the socket command.
016: **/
017:
018: public class ServerSocketChannel extends Channel {
019:
020: /**
021: * The java ServerSocket object associated with this Channel.
022: **/
023:
024: private ServerSocket sock;
025:
026: /**
027: * The interpreter to evaluate the callback in, when a connection
028: * is made.
029: **/
030:
031: private Interp cbInterp;
032:
033: /**
034: * The script to evaluate in the interpreter.
035: **/
036:
037: private TclObject callback;
038:
039: /**
040: * The thread which listens for new connections.
041: **/
042:
043: private AcceptThread acceptThread;
044:
045: /**
046: * Creates a new ServerSocketChannel object with the given options.
047: * Creates an underlying ServerSocket object, and a thread to handle
048: * connections to the socket.
049: **/
050:
051: public ServerSocketChannel(Interp interp, String localAddr,
052: int port, TclObject callback) throws TclException {
053: InetAddress localAddress = null;
054:
055: // Resolve address (if given)
056: if (!localAddr.equals("")) {
057: try {
058: localAddress = InetAddress.getByName(localAddr);
059: } catch (UnknownHostException e) {
060: throw new TclException(interp, "host unkown: "
061: + localAddr);
062: }
063: }
064: this .mode = TclIO.CREAT; // Allow no reading or writing on channel
065: this .callback = callback;
066: this .cbInterp = interp;
067:
068: // Create the server socket.
069: try {
070: if (localAddress == null)
071: sock = new ServerSocket(port);
072: else
073: sock = new ServerSocket(port, 0, localAddress);
074: } catch (IOException ex) {
075: throw new TclException(interp, ex.getMessage());
076: }
077:
078: acceptThread = new AcceptThread(sock, this );
079:
080: setChanName(TclIO.getNextDescriptor(interp, "sock"));
081: acceptThread.start();
082: }
083:
084: synchronized void addConnection(Socket s) {
085: // Create an event which executes the callback TclString with
086: // the arguments sock, addr, port added.
087: // First, create a SocketChannel for this Socket object.
088: SocketChannel sChan = null;
089: try {
090: sChan = new SocketChannel(cbInterp, s);
091: // Register this channel in the channel tables.
092: TclIO.registerChannel(cbInterp, sChan);
093: } catch (Exception e) {
094: e.printStackTrace();
095: }
096: SocketConnectionEvent evt = new SocketConnectionEvent(cbInterp,
097: callback, sChan.getChanName(), s.getInetAddress()
098: .getHostAddress(), s.getPort());
099: cbInterp.getNotifier().queueEvent((TclEvent) evt,
100: TCL.QUEUE_TAIL);
101: }
102:
103: // FIXME: Since this does not actually close the socket
104: // right away, we run into errors in the test suite
105: // saying the socket is already in use after the close
106: // command has been issued. Need to figure out how to
107: // deal with this issue.
108:
109: void close() throws IOException {
110: // Stop the event handler thread.
111: // this might not happen for up to a minute!
112: acceptThread.pleaseStop();
113:
114: super .close();
115: sock.close();
116: }
117:
118: /**
119: * Override to provide specific errors for server socket.
120: **/
121:
122: void seek(Interp interp, long offset, int mode) throws IOException,
123: TclException {
124: throw new TclPosixException(interp, TclPosixException.EACCES,
125: true, "error during seek on \"" + getChanName() + "\"");
126: }
127:
128: String getChanType() {
129: return "tcp";
130: }
131:
132: protected InputStream getInputStream() throws IOException {
133: throw new RuntimeException("should never be called");
134: }
135:
136: protected OutputStream getOutputStream() throws IOException {
137: throw new RuntimeException("should never be called");
138: }
139: }
140:
141: class AcceptThread extends Thread {
142:
143: private ServerSocket sock;
144: private ServerSocketChannel sschan;
145: boolean keepRunning;
146:
147: public AcceptThread(ServerSocket s1, ServerSocketChannel s2) {
148: sock = s1;
149:
150: // Every 10 seconds, we check to see if this socket has been closed:
151: try {
152: sock.setSoTimeout(10000);
153: } catch (SocketException e) {
154: }
155:
156: sschan = s2;
157: keepRunning = true;
158: }
159:
160: public void run() {
161: try {
162: while (keepRunning) {
163: Socket s = null;
164: try {
165: s = sock.accept();
166: } catch (InterruptedIOException ex) {
167: // Timeout
168: continue;
169: } catch (IOException ex) {
170: // Socket closed
171: break;
172: }
173: // Get a connection
174: sschan.addConnection(s);
175: }
176: } catch (Exception e) {
177: // Something went wrong.
178: e.printStackTrace();
179: }
180: }
181:
182: public void pleaseStop() {
183: keepRunning = false;
184: }
185: }
|