ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/cms/source/server/uk/org/iscream/cms/server/clientinterface/TCPControlHandler.java
Revision: 1.12
Committed: Thu Feb 1 00:18:13 2001 UTC (23 years, 4 months ago) by tdb
Branch: MAIN
Changes since 1.11: +79 -62 lines
Log Message:
Neatened the message sending to the client by moving common bits to private
methods. It's a touch easier to follow the code now.

File Contents

# Content
1 //---PACKAGE DECLARATION---
2 package uk.ac.ukc.iscream.clientinterface;
3
4 //---IMPORTS---
5 import uk.ac.ukc.iscream.util.*;
6 import uk.ac.ukc.iscream.componentmanager.*;
7 import uk.ac.ukc.iscream.core.*;
8 import java.net.Socket;
9 import java.net.ServerSocket;
10 import java.io.InputStream;
11 import java.io.OutputStream;
12 import java.io.IOException;
13 import java.io.BufferedReader;
14 import java.io.PrintWriter;
15 import java.io.InputStreamReader;
16
17
18 /**
19 * Acts as a Control Handler to a TCP based client.
20 *
21 * @author $Author: tdb1 $
22 * @version $Id: TCPControlHandler.java,v 1.11 2001/01/29 02:01:45 tdb1 Exp $
23 */
24 class TCPControlHandler extends Thread {
25
26 //---FINAL ATTRIBUTES---
27
28 /**
29 * The current CVS revision of this class
30 */
31 public final String REVISION = "$Revision: 1.11 $";
32
33 /**
34 * This is our protocol version. It is hardcoded to ensure
35 * that it doesn't get accidently changed. A change to the
36 * protocol would also require changing this classes code !
37 */
38 public static final String PROTOVER = "PROTOCOL 1.1";
39
40 //---STATIC METHODS---
41
42 //---CONSTRUCTORS---
43
44 /**
45 * Construct a new TCPControlHandler, and setup the reader
46 * and writer for the new Socket.
47 *
48 * @param socket The Socket connected to the new Client
49 * @param packetSorter A reference to the PacketSorter in the component
50 */
51 public TCPControlHandler(Socket socket, PacketSorter packetSorter) throws IOException {
52 _socket = socket;
53 _packetSorter = packetSorter;
54 // setup the reader & writer
55 _socketIn = new BufferedReader(new InputStreamReader(_socket.getInputStream()));
56 _socketOut = new PrintWriter(_socket.getOutputStream());
57 // set the default hostlist to "all host", for backward compatibility
58 _hostList = "";
59 _logger.write(toString(), Logger.SYSINIT, "created");
60 }
61
62 //---PUBLIC METHODS---
63
64 /**
65 * This method initiates the thread, setting things up, and then
66 * reading commands from the Client. It handles setting up of the
67 * DataHandler, and clean shutting down.
68 */
69 public void run() {
70 boolean run = true;
71 // Tell the client our protocol, and ask it for it's name
72 try {
73 send(PROTOVER);
74 _clientName = _socketIn.readLine();
75 if(_clientName==null) {
76 throw new IOException("Fatal error reading from client");
77 }
78 sendOK();
79 }
80 catch(IOException e) {
81 _logger.write(toString(), Logger.FATAL, "Fatal error, shutdown pending");
82 run=false;
83 }
84
85 _logger.write(toString(), Logger.DEBUG, "Client has connected: "+_clientName);
86
87 // loop until we decide to shutdown (run=false)
88 while(run) {
89 try {
90 String cmd = _socketIn.readLine();
91 if(cmd==null) {
92 throw new IOException("Fatal error reading from client");
93 }
94 // make a decision about what to do
95 if(cmd.equals("STARTCONFIG")) {
96 // get the configuration for this client
97 Configuration myConfig = _configManager.getConfiguration("Client."+_clientName);
98 sendOK();
99 // get properties
100 cmd = _socketIn.readLine();
101 if(cmd==null) {
102 throw new IOException("Fatal error reading from client");
103 }
104 // provide all the requested properties
105 while(!cmd.equals("ENDCONFIG")) {
106 // client is restricted to this properties
107 if(cmd.startsWith("Client.") || cmd.startsWith("Host.")) {
108 try {
109 String returnedProperty = myConfig.getProperty(cmd);
110 send(returnedProperty);
111 }
112 catch (org.omg.CORBA.MARSHAL e) {
113 sendERROR();
114 }
115 }
116 else {
117 sendERROR();
118 }
119 cmd = _socketIn.readLine();
120 if(cmd==null) {
121 throw new IOException("Fatal error reading from client");
122 }
123 }
124 sendOK();
125 _logger.write(toString(), Logger.DEBUG, "Client has been configured");
126
127 }
128 else if(cmd.equals("STARTDATA")) {
129 // if we don't have a DataHandler, set one up
130 if(_dataHandler == null) {
131 // create a serversocket
132 ServerSocket ss = new ServerSocket(0);
133 // get the port
134 String port = new Integer(ss.getLocalPort()).toString();
135 // tell the client the port
136 send(port);
137 // wait for the client to connect
138 Socket s = ss.accept();
139 // when we get the Socket back, give it to the data thread
140 TCPDataHandler dh = new TCPDataHandler(s);
141 // register the DataHandler's queue, giving the host list
142 _packetSorter.register(dh.getQueue(), _hostList);
143 // start up the DataHandler
144 dh.start();
145 // Hold a reference to the DataHandler, so we can stop it later
146 _dataHandler = dh;
147 sendOK();
148 _logger.write(toString(), Logger.DEBUG, "Data stream started at Clients Request on port: "+port);
149 }
150 else {
151 sendERROR();
152 }
153 }
154 else if(cmd.equals("STOPDATA")) {
155 // attempt to close the data channel
156 if(closeData()) {
157 sendOK();
158 _logger.write(toString(), Logger.DEBUG, "Data stream stopped at Clients Request");
159 }
160 else {
161 sendERROR();
162 }
163 }
164 else if(cmd.equals("SETHOSTLIST")) {
165 // we can only set the host list when
166 // the DataHandler is not connected
167 if(_dataHandler == null) {
168 sendOK();
169 // read and set the host list
170 cmd = _socketIn.readLine();
171 if(cmd==null) {
172 throw new IOException("Fatal error reading from client");
173 }
174 _hostList = cmd;
175 sendOK();
176 }
177 else {
178 sendERROR();
179 }
180 }
181 else if(cmd.equals("DISCONNECT")) {
182 // we going to disconnect, so lets stop the main loop
183 run=false;
184 // if there is a DataHandler, we'd best shut it down
185 if(closeData()) {
186 _logger.write(toString(), Logger.DEBUG, "Data stream stopped at Clients Request");
187 }
188 // say bye to the client
189 sendOK();
190 // close the reader, writer and Socket
191 _socketIn.close();
192 _socketOut.close();
193 _socket.close();
194 _logger.write(toString(), Logger.DEBUG, "Closing at Clients Request");
195 }
196 else {
197 sendERROR();
198 }
199 }
200 catch(IOException e) {
201 // if we get an exception, the client has probably left, so we stop
202 run=false;
203 _logger.write(toString(), Logger.FATAL, "Fatal communication error, shutdown pending");
204 }
205 }
206 _logger.write(toString(), Logger.DEBUG, "Shutting down Control Handler, client has gone.");
207 }
208
209 /**
210 * Overrides the {@link java.lang.Object#toString() Object.toString()}
211 * method to provide clean logging (every class should have this).
212 *
213 * This uses the uk.ac.ukc.iscream.util.NameFormat class
214 * to format the toString()
215 *
216 * @return the name of this class and its CVS revision
217 */
218 public String toString() {
219 return FormatName.getName(
220 _name,
221 getClass().getName(),
222 REVISION);
223 }
224
225 //---PRIVATE METHODS---
226
227 /**
228 * Attempt to close down the DataHandler. This will return
229 * true if it managed to close it down, or false if there
230 * wasn't a DataHandler to close.
231 *
232 * @return whether the channel could be closed
233 */
234 private boolean closeData() {
235 // if we have a DataHandler, shut it down
236 if(_dataHandler != null) {
237 // Deregister the DataHandler, giving the host list
238 _packetSorter.deregister(_dataHandler.getQueue(), _hostList);
239 // Shut down the data handler
240 _dataHandler.shutdown();
241 // Destroy our reference, leaving it for the GC
242 _dataHandler = null;
243 return true;
244 }
245 else {
246 return false;
247 }
248 }
249
250 /**
251 * Send an OK message to the outgoing Socket
252 */
253 private void sendOK() {
254 send("OK");
255 }
256
257 /**
258 * Send an ERROR message to the outgoing Socket.
259 */
260 private void sendERROR() {
261 send("ERROR");
262 }
263
264 /**
265 * Send any String message to the outgoing Socket.
266 *
267 * @param message The message/command to send.
268 */
269 private void send(String message) {
270 _socketOut.println(message);
271 _socketOut.flush();
272 }
273
274 //---ACCESSOR/MUTATOR METHODS---
275
276 //---ATTRIBUTES---
277
278 /**
279 * This is the friendly identifier of the
280 * component this class is running in.
281 * eg, a Filter may be called "filter1",
282 * If this class does not have an owning
283 * component, a name from the configuration
284 * can be placed here. This name could also
285 * be changed to null for utility classes.
286 */
287 private String _name = ClientInterfaceMain.NAME;
288
289 /**
290 * This holds a reference to the
291 * system logger that is being used.
292 */
293 private Logger _logger = ReferenceManager.getInstance().getLogger();
294
295 /**
296 * A reference to the Configuration Manager the system is using
297 */
298 private ConfigurationManager _configManager = ReferenceManager.getInstance().getCM();
299
300 /**
301 * The socket we are talking on
302 */
303 private Socket _socket;
304
305 /**
306 * A hook to the inbound data from the socket
307 */
308 private BufferedReader _socketIn;
309
310 /**
311 * A hook to the outbound stream for the socket
312 */
313 private PrintWriter _socketOut;
314
315 /**
316 * A reference to the PacketSorter.
317 */
318 private PacketSorter _packetSorter;
319
320 /**
321 * A reference to the DataHandler, if there is one
322 */
323 private TCPDataHandler _dataHandler;
324
325 /**
326 * The name of the Client we're connected to
327 */
328 private String _clientName;
329
330 /**
331 * The host list the Client has requested
332 */
333 private String _hostList;
334
335 //---STATIC ATTRIBUTES---
336
337 }