ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/cms/source/conient/uk/org/iscream/cms/conient/ConnectionHandler.java
Revision: 1.26
Committed: Mon Mar 19 02:49:45 2001 UTC (23 years, 2 months ago) by ajm
Branch: MAIN
Changes since 1.25: +48 -38 lines
Log Message:
All configuration for hosts is now obtained individually, rather than globally.  This is how it should be.

All the configuration options and classes have been tidied.

File Contents

# Content
1 //---PACKAGE DECLARATION---
2 package uk.org.iscream.conient;
3
4 //---IMPORTS---
5 import uk.org.iscream.util.*;
6 import java.io.*;
7 import java.net.*;
8 import javax.swing.JOptionPane;
9
10 /**
11 * This is the main thread for the client.
12 * Once started i continually checks it actionQueue
13 * for actions that other areas of the system have placed
14 * there, it then performs those actions.
15 *
16 * Currently this is the main thread where all non-GUI events
17 * are dispatched to.
18 *
19 * @author $Author: ajm4 $
20 * @version $Id: ConnectionHandler.java,v 1.25 2001/03/18 17:40:41 ajm4 Exp $
21 */
22 public class ConnectionHandler extends Thread {
23
24 //---FINAL ATTRIBUTES---
25
26 /**
27 * The current CVS revision of this class
28 */
29 public final String REVISION = "$Revision: 1.25 $";
30
31
32 /**
33 * The hardcoded protocol version that we are using.
34 * Used when handshaking with the server
35 */
36 public final double PROTOCOL_VERSION = 1.1;
37
38 /**
39 * Thread action DONOTHING.
40 * This is an invalid action, but here for completeness
41 * if anything sends this action it will warn appropriately
42 * but do what it says, ie, nothing ;)
43 */
44 public static final int DONOTHING = 0;
45
46 /**
47 * Thread action CONNECT.
48 * Opens the control link to the i-scream server.
49 */
50 public static final int CONNECT = 1;
51
52 /**
53 * Thread action STARTDATA.
54 * Opens the data link to the i-scream server
55 * and prepares all gui data components for data
56 * it then starts all relavant threads going.
57 */
58 public static final int STARTDATA = 2;
59
60 /**
61 * Thread action STOPDATA.
62 * Closes the data link and shuts down all components
63 * that update gui for various things.
64 */
65 public static final int STOPDATA = 3;
66
67 /**
68 * Thread action DISCONNECT.
69 * Checks to see if STOPDATA has been called if not,
70 * it will add STOPDATA and then DISCONNECT to
71 * the action queue and return. If STOPDATA has been
72 * called it closes down the Control Link and tidies up
73 * all relevant threads
74 */
75 public static final int DISCONNECT = 4;
76
77 /**
78 * Thread action QUIT.
79 * Checks the status of the two links, if either
80 * are still up, it queues the appropriate commands
81 * to close them down. It then System.exit(0)'s!
82 */
83 public static final int QUIT = 5;
84
85 /**
86 * Thread action GETCONFIGURATION
87 * Starts the command to obtain the configuration
88 * from the server.
89 * It then passes the IO to the Configuration object
90 * so it can obtain any specific configuration
91 */
92 public static final int GETCONFIGURATION = 6;
93
94 /**
95 * This is the time in seconds that this class
96 * should wait on the DataReader class to fully
97 * shutdown after calling shutdown() before
98 * forcing a shutdown
99 */
100 public static final int DATAREADER_SHUTDOWN_TIMEOUT = 5;
101
102 /**
103 * The default local server to connect to when a
104 * firewall is in use if one is not specified in the server
105 */
106 public static final String DEFAULT_FIREWALL_SERVER = "localhost";
107
108 /**
109 * The default time in seconds to wait for the
110 * firewall setup command to execute
111 */
112 public static final int DEFAULT_FIREWALL_COMMANDWAIT = 5;
113
114 //---STATIC METHODS---
115
116 //---CONSTRUCTORS---
117
118 /**
119 * Constructs new data handler.
120 * Needs a reference to the data panel so that
121 * it can set it processing inBound data
122 * Also gets a reference to the queue that will be
123 * used by other areas of the system to send it actions
124 *
125 * @param data the DataPanel in use
126 * @param actionQueue the actionQueue for this class
127 */
128 public ConnectionHandler(DataPanel data, Queue actionQueue) {
129 _data = data;
130 _actionQueue = actionQueue;
131 _myQueue = _actionQueue.getQueue();
132 _configuration.setConnectionHandler(this);
133 }
134
135 //---PUBLIC METHODS---
136
137
138 /**
139 * Starts this ConnectionHandler running.
140 * This basically runs until told to stop, it gets "actions"
141 * from its actionQueue and switch's on them do determine
142 * what it should do it then carries out the action
143 *
144 * For details on what each action does see the action
145 * types.
146 */
147 public void run() {
148 if(_configuration.getProperty("control.onstartconnect").equals("1")) {
149 _actionQueue.add(new Integer(CONNECT));
150 }
151 if(_configuration.getProperty("data.onstartconnect").equals("1")) {
152 _actionQueue.add(new Integer(STARTDATA));
153 }
154 while(_running) {
155 // we wait for a call...
156 int action = 0;
157 try {
158 action = ((Integer) _actionQueue.get(_myQueue)).intValue();
159 } catch (InvalidQueueException e) {
160 // we 're never going to get this
161 // but if we do we should do something nasty
162 throw new RuntimeException("unable to retrieve events from actionQueue!");
163 }
164
165 // examine our action...
166 // if it was to connect...then we connect...
167 switch(action) {
168 case CONNECT:
169 if (_controlLink == null) {
170 try {
171 // get the server name from the config
172 _configuredServer = _configuration.getProperty("control.server");
173
174 if (_configuredServer == null) {
175 throw new IOException("no i-scream server in current configuration");
176 }
177
178 // open the socket to the server and bind the IO
179 // get the port from the config
180 String portString = _configuration.getProperty("control.port");
181 if (portString == null) {
182 throw new IOException("no i-scream server port in current configuration");
183 }
184 int port = 0;
185 try {
186 port = Integer.parseInt(portString);
187 } catch (NumberFormatException e) {
188 throw new IOException("no valid i-scream server port in current configuration");
189 }
190
191 // start firewall if needed
192 _server = handleFirewall(_configuredServer, port, _controlFirewallProcess);
193
194 Conient.setControlStatus("Connecting to - " + _server);
195 _controlLink = new Socket(_server, port);
196 _inBound = new BufferedReader(new InputStreamReader(_controlLink.getInputStream()));
197 _outBound = new PrintWriter(_controlLink.getOutputStream());
198 Conient.setControlStatus("Connection Established - " + _server);
199
200 String response;
201 response = _inBound.readLine();
202
203 // check the servers Protocol Identity against our own
204 // we SHOULD be backwards compatible, so we can continue if
205 // they are using a newer protocol, anything else then we die
206 double serverProtocolVersion = Double.parseDouble(response.substring(9, response.length()));
207 Conient.addMessage("Protocol Versions: server [" + serverProtocolVersion + "] client [" + PROTOCOL_VERSION + "]");
208 if (serverProtocolVersion > PROTOCOL_VERSION) {
209 Conient.addMessage("WARNING{control link}: server is using a newer protocol (" + response + "), please update your client, continuing with old protocol (PROTOCOL " + PROTOCOL_VERSION + ")" );
210 } else if (serverProtocolVersion < PROTOCOL_VERSION) {
211 // tidy up
212 throw new IOException("incompatible protocol version");
213 }
214
215 // send the name of the client
216 _outBound.println(_configuration.getProperty("clientname"));
217 _outBound.flush();
218 response = _inBound.readLine();
219 if (!response.equals("OK")) {
220 // tidy up
221
222 throw new IOException("client name rejected - " + _configuration.getProperty("clientname"));
223 }
224
225 } catch (IOException e) {
226 // print the error and tidy up what's left
227 Conient.addMessage("ERROR{control link}: " + e);
228 _controlLink = null;
229 // and the firewall handler if there is one
230 closeFirewall(_controlFirewallProcess);
231 _actionQueue.clearQueue(_myQueue);
232 Conient.setControlStatus("Disconnected");
233 }
234 } else {
235 Conient.addMessage("WARNING{control link}: already established");
236 }
237 break;
238 case STARTDATA:
239 // as long as the data link hasn't been established
240 // we want to establish it
241 if (_dataLink == null) {
242 // check that the control link is open, if it isn't we
243 // might want to sort that problemo out
244 // we do this by simply queueing the event to occour, then
245 // this event to run again ;-)
246 if(_controlLink == null) {
247 Conient.addMessage("WARNING{data link}: control link not established - queueing start events");
248 _actionQueue.add(new Integer(CONNECT));
249 _actionQueue.add(new Integer(STARTDATA));
250 } else {
251 try {
252 // set our host list if we know we have one we need to set
253 String hostList = _configuration.getProperty("hostList");
254 boolean hostListSet = false;
255 // send our hostList if we have
256 if (_configuration.getProperty("useHostList").equals("1")) {
257 if (hostList.equals("")) {
258 Conient.addMessage("WARNING{control link}: your host list is empty, the server will send ALL hosts");
259 }
260 hostListSet = setHostList(hostList);
261 // if not, indicate we want the lot
262 } else {
263 hostListSet = setHostList("");
264 }
265 // warn if there was a problem, it will have already error'd
266 if (!hostListSet) {
267 Conient.addMessage("WARNING{control link}: unable to set host list");
268 }
269 // ask the server to start the data link
270 String response;
271 _outBound.println("STARTDATA");
272 _outBound.flush();
273 response = _inBound.readLine();
274
275 // see if the server suggested a good port
276 if (response.equals("ERROR")) {
277 throw new IOException("server unable to start data link at this time");
278 }
279 int port = 0;
280 try {
281 port = Integer.parseInt(response);
282 } catch (NumberFormatException e) {
283 throw new IOException("invalid data port suggested by server - " + response);
284 }
285
286 // start firewall if needed
287 _server = handleFirewall(_configuredServer, port, _dataFirewallProcess);
288 Conient.setDataStatus("Connecting to - " + _server + ":" + response);
289
290 _dataLink = new Socket(_server, port);
291
292 response = _inBound.readLine();
293 if (!response.equals("OK")) {
294 throw new IOException("server reported error establishing data channel");
295 }
296
297 // if the socket was ok, then we attack our IO hooks
298 _dataInBound = new BufferedReader(new InputStreamReader(_dataLink.getInputStream()));
299 _dataOutBound = new PrintWriter(_dataLink.getOutputStream());
300 Conient.setDataStatus("Connection Established - " + _server);
301 // now we want to start reading the data in
302 // so we start the appropriate components on their way
303 // we create a queue to give to both the reader and the
304 // displayer
305 Queue theQueue = new Queue();
306 _dataReader = new DataReader(_dataInBound, theQueue);
307 _data.setQueue(theQueue);
308 _data.cleanUpTabs();
309
310 // start the data rocking
311 new Thread(_data).start();
312 _dataReader.start();
313 // finished for us....
314 } catch (IOException e) {
315 // print the error and tidy up what's left
316 Conient.addMessage("ERROR{data link}: " + e);
317 _dataLink = null;
318 // and the firewall handler if there is one
319 closeFirewall(_dataFirewallProcess);
320 _actionQueue.clearQueue(_myQueue);
321 Conient.setDataStatus("Disconnected");
322 }
323 }
324 } else {
325 Conient.addMessage("WARNING{data link}: already established");
326 }
327 break;
328 case STOPDATA:
329 if(_dataLink != null) {
330 try {
331 String response;
332 // shut down the data link
333 Conient.setDataStatus("Disconnecting - " + _server);
334
335 // close the reader
336 _dataReader.shutdown();
337 // wait for it to close
338 boolean dirtyShutdown = true;
339 long startTime = System.currentTimeMillis();
340 while((System.currentTimeMillis() - startTime) < (DATAREADER_SHUTDOWN_TIMEOUT * 1000)) {
341 if (!_dataReader.isAlive()) {
342 dirtyShutdown = false;
343 break;
344 }
345 }
346 // warn if it didn't shutdown in time
347 if (dirtyShutdown) {
348 Conient.addMessage("WARNING{data link}: data reader thread did not close within timeout, killing its IO anyway!");
349 }
350
351 // tell the server
352 _outBound.println("STOPDATA");
353 _outBound.flush();
354 response = _inBound.readLine();
355
356 // check the server was ok with our request...
357 // even if it wasn't we will go anyway!
358 if (!response.equals("OK")) {
359 throw new IOException("server didn't OK request to stop data channel - stopping anyway");
360 }
361
362
363 // close the lot down
364 _dataInBound.close();
365 _dataOutBound.close();
366 _dataLink.close();
367 // get rid of the socket
368 _dataLink = null;
369
370 // and the firewall handler if there is one
371 closeFirewall(_dataFirewallProcess);
372
373 Conient.setDataStatus("Disconnected");
374 } catch (IOException e) {
375 // print the error and tidy up what's left
376 Conient.addMessage("ERROR{data link}: " + e);
377 try {
378 _dataOutBound.close();
379 _dataInBound.close();
380 _dataLink.close();
381 // and the firewall handler if there is one
382 closeFirewall(_dataFirewallProcess);
383 } catch (IOException e2) {
384 Conient.addMessage("CRITICAL{control link}: unable to close socket - " + e2);
385 }
386 _dataLink = null;
387 _actionQueue.clearQueue(_myQueue);
388 Conient.setDataStatus("Disconnected");
389 }
390 } else {
391 Conient.addMessage("WARNING{data link}: already disconnected");
392 }
393 break;
394 case DISCONNECT:
395 if (_controlLink != null) {
396 if (_dataLink != null) {
397 // we want to tell ourselves to stop it
398 Conient.addMessage("WARNING{control link}: data link not disconnected - queueing stop events");
399 _actionQueue.add(new Integer(STOPDATA));
400 _actionQueue.add(new Integer(DISCONNECT));
401 } else {
402 try {
403 // request the server to disconnect
404 String response;
405 _outBound.println("DISCONNECT");
406 _outBound.flush();
407 response = _inBound.readLine();
408
409 // check the server was ok with our request...
410 // even if it wasn't we will go anyway!
411 if (!response.equals("OK")) {
412 throw new IOException("server didn't OK request to stop control channel - stopping anyway");
413 }
414
415 // then lets shutdown the link
416 Conient.setControlStatus("Disconnecting - " + _server);
417 _inBound.close();
418 _outBound.close();
419 _controlLink.close();
420 // for good measure
421 _controlLink = null;
422
423 // and the firewall handler if there is one
424 closeFirewall(_controlFirewallProcess);
425
426 Conient.setControlStatus("Disconnected");
427 } catch (IOException e) {
428 Conient.addMessage("ERROR{control link}: " + e);
429 try {
430 _inBound.close();
431 _outBound.close();
432 _controlLink.close();
433 // and the firewall handler if there is one
434 closeFirewall(_controlFirewallProcess);
435 } catch (IOException e2) {
436 Conient.addMessage("CRITICAL{control link}: unable to close socket - " + e2);
437 }
438 _controlLink = null;
439 _actionQueue.clearQueue(_myQueue);
440 Conient.setControlStatus("Disconnected");
441 }
442 }
443 } else {
444 Conient.addMessage("WARNING{control link}: already disconnected");
445 }
446 break;
447 case QUIT:
448 Conient.addMessage("Exiting.");
449 try {
450 // stop data and control if data up
451 if (_dataLink != null) {
452 _actionQueue.add(new Integer(STOPDATA));
453 _actionQueue.add(new Integer(DISCONNECT));
454 _actionQueue.add(new Integer(QUIT));
455 throw new IOException();
456 }
457 // stop control
458 if (_controlLink != null) {
459 _actionQueue.add(new Integer(DISCONNECT));
460 _actionQueue.add(new Integer(QUIT));
461 throw new IOException();
462 }
463 Conient.addMessage("Finished.");
464 // go!
465 System.exit(0);
466 } catch (IOException e) {
467 Conient.addMessage("WARNING: open connections detected - queueing stop events");
468 }
469 break;
470 }
471 }
472 }
473
474 /**
475 * This method is called by the Configuration object
476 * when a request is made for a server property.
477 * The Configuration object is informed of the Connection
478 * Handler's whereabouts on construction of the ConnectionHandler.
479 *
480 * This implements the STARTCONFIG command in the 1.0 protocol.
481 */
482 public String getConfigFromServer(String configName, String propertyName) {
483 String property = "";
484 if(_controlLink != null) {
485 try {
486 Conient.addMessage("Getting configuration from server");
487 String response = null;
488 _outBound.println("STARTCONFIG");
489 _outBound.flush();
490 response = _inBound.readLine();
491 if (!response.equals("OK")) {
492 throw new IOException("server refused (" + response + ")");
493 }
494
495 // **** TEMPORARY UNTIL NEXT SERVER BOOT ****
496 // NEW LINE = _outBound.println(configName + ";" + propertyName);
497 _outBound.println(propertyName);
498 //**********************************************
499 _outBound.flush();
500 response = _inBound.readLine();
501 if (response.equals("ERROR")) {
502 throw new IOException("server did not returned ERROR");
503 }
504 property = response;
505 _outBound.println("ENDCONFIG");
506 _outBound.flush();
507 response = _inBound.readLine();
508 if (!response.equals("OK")) {
509 throw new IOException("server reported error when finishing configuration");
510 }
511 } catch (IOException e) {
512 Conient.addMessage("ERROR{control link}: when getting configuration - " + e);
513 }
514 }
515 return property;
516 }
517
518 /**
519 * This method allows other classes
520 * to shutdown this connection handler.
521 */
522 public void shutdown() {
523 _running = false;
524 }
525
526 //---PRIVATE METHODS---
527
528 /**
529 * This method performs the SETHOSTLIST command.
530 * This is run by the CONNECT command.
531 * It tells the server which hosts we are interested
532 * in, if "" is sent, this indicates we want ALL hosts.
533 *
534 * @param hostList the list of hosts as gained from the config
535 * @return if we succeeded in setting the host list
536 */
537 private boolean setHostList(String hostList) {
538 boolean success = false;
539 // must have a control link open
540 if (_controlLink != null) {
541 // data link must be closed (according to 1.1 PROTOCOL)
542 if(_dataLink == null) {
543 try {
544 Conient.addMessage("Setting host list to:" + hostList);
545 String response = null;
546 _outBound.println("SETHOSTLIST");
547 _outBound.flush();
548 response = _inBound.readLine();
549 if (!response.equals("OK")) {
550 throw new IOException("server refused - data link possibly still open?");
551 }
552 _outBound.println(hostList);
553 _outBound.flush();
554 response = _inBound.readLine();
555 if (!response.equals("OK")) {
556 throw new IOException("server had trouble with our request");
557 }
558 success = true;
559 } catch (IOException e) {
560 Conient.addMessage("ERROR{control link}: when setting hostlist - " + e);
561 }
562 }
563 }
564 return success;
565 }
566
567 /**
568 * Handles opening pipes through firewalls.
569 * It checks the configuration for various entries
570 * to set up the link or not.
571 *
572 * If a link is setup it will return the name of the
573 * new server to connect to, if a link is NOT setup,
574 * it will simply return the original server name.
575 *
576 * The name of the machine that the pipe is setup on
577 * defaults to "localhost", unless the configuration
578 * specifies otherwise.
579 *
580 * Once it has run the firewall command it then waits
581 * a set period according to the config for the firewall
582 * pipe to be set up.
583 *
584 * The firewall process should be destroyed when the
585 * link is finished with.
586 *
587 * @param server the name of the server to open up the pipe to
588 * @param port the port number to open up the pipe to
589 * @param firewallProcess the holder for the new firewall process
590 * @return the server to connect to, as determined by this routine
591 */
592 private String handleFirewall(String server, int port, Process firewallProcess) {
593 String firewallCommand = _configuration.getProperty("firewall.command");
594 String useFirewall = _configuration.getProperty("useFirewall");
595 String firewallCommandWait = _configuration.getProperty("firewall.commandwait");
596 String firewallServer = _configuration.getProperty("firewall.server");
597 // if we are running firewall support...then lets start it
598 if (useFirewall.equals("1")) {
599 // clean up the command with what we want
600 firewallCommand = StringUtils.replaceText(firewallCommand, "%PORT%", new Integer(port).toString());
601 firewallCommand = StringUtils.replaceText(firewallCommand, "%SERVER%", server);
602 Conient.addMessage("WARNING{firewall}: firewall pipes requested, running pipe setup command \"" + firewallCommand + "\"");
603 try {
604 // run the command
605 firewallProcess = Runtime.getRuntime().exec(firewallCommand);
606
607 // work out how long we should wait before carrying on
608 int time = 0;
609 try {
610 time = Integer.parseInt(firewallCommandWait);
611 } catch (NumberFormatException e) {
612 time = 0;
613 }
614 if (time == 0) {
615 time = DEFAULT_FIREWALL_COMMANDWAIT;
616 }
617 Conient.addMessage("WARNING{firewall}: waiting " + time + " seconds for command to complete!");
618 // wait for the command to finish
619 try {
620 Thread.sleep(time * 1000);
621 } catch (InterruptedException e) {
622 }
623
624 // set the server we want to return
625 server = firewallServer;
626 if (server.equals("")) {
627 server = DEFAULT_FIREWALL_SERVER;
628 }
629 } catch (IOException e) {
630 Conient.addMessage("ERROR{firewall}: unable to start pipe to i-scream server");
631 }
632 }
633 return server;
634 }
635
636 /**
637 * Checks to see if the given firewall process
638 * has been created. If it has it calls destroy()
639 * on it.
640 *
641 * @param firewallProcess the process to check
642 */
643 private void closeFirewall(Process firewallProcess) {
644 if (firewallProcess != null) {
645 firewallProcess.destroy();
646 firewallProcess = null;
647 Conient.addMessage("WARNING{firewall}: firewall process destroyed");
648 }
649 }
650
651 //---ACCESSOR/MUTATOR METHODS---
652
653 //---ATTRIBUTES---
654
655 /**
656 * The state if this thread
657 */
658 private boolean _running = true;
659
660 /**
661 * A reference to the displaying class DataPanel
662 */
663 private DataPanel _data;
664
665 /**
666 * The queue that actions are added to by other parts of the system
667 */
668 private Queue _actionQueue;
669
670 /**
671 * The queue number that we are reading from on the action queue
672 */
673 private int _myQueue;
674
675 /**
676 * The control link socket
677 */
678 private Socket _controlLink;
679
680 /**
681 * The data link socket
682 */
683 private Socket _dataLink;
684
685 /**
686 * The input for the control link
687 */
688 private BufferedReader _inBound;
689
690 /**
691 * The output for the control link
692 */
693 private PrintWriter _outBound;
694
695 /**
696 * The input for the data link
697 */
698 private BufferedReader _dataInBound;
699
700 /**
701 * The output for the data link
702 */
703 private PrintWriter _dataOutBound;
704
705 /**
706 * A reference to the DataReader in use
707 */
708 private DataReader _dataReader;
709
710 /**
711 * A reference to the system configuration component
712 */
713 private Configuration _configuration = Configuration.getInstance();
714
715 /**
716 * The server we will be connecting to
717 */
718 private String _server = null;
719
720 /**
721 * The server that the config says we should connect to
722 */
723 private String _configuredServer = null;
724
725 /**
726 * The process used to start the firewall pipe
727 * for the control link.
728 */
729 private Process _controlFirewallProcess = null;
730
731 /**
732 * The process used to start the firewall pipe
733 * for the data link.
734 */
735 private Process _dataFirewallProcess = null;
736
737 //---STATIC ATTRIBUTES---
738
739 }