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.13
Committed: Wed Feb 7 13:47:33 2001 UTC (23 years, 3 months ago) by tdb
Branch: MAIN
Changes since 1.12: +3 -4 lines
Log Message:
Made use of the autoflush feature of PrintWriter.

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.12 2001/02/01 00:18:13 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.12 $";
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(), true);
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 }
272
273 //---ACCESSOR/MUTATOR METHODS---
274
275 //---ATTRIBUTES---
276
277 /**
278 * This is the friendly identifier of the
279 * component this class is running in.
280 * eg, a Filter may be called "filter1",
281 * If this class does not have an owning
282 * component, a name from the configuration
283 * can be placed here. This name could also
284 * be changed to null for utility classes.
285 */
286 private String _name = ClientInterfaceMain.NAME;
287
288 /**
289 * This holds a reference to the
290 * system logger that is being used.
291 */
292 private Logger _logger = ReferenceManager.getInstance().getLogger();
293
294 /**
295 * A reference to the Configuration Manager the system is using
296 */
297 private ConfigurationManager _configManager = ReferenceManager.getInstance().getCM();
298
299 /**
300 * The socket we are talking on
301 */
302 private Socket _socket;
303
304 /**
305 * A hook to the inbound data from the socket
306 */
307 private BufferedReader _socketIn;
308
309 /**
310 * A hook to the outbound stream for the socket
311 */
312 private PrintWriter _socketOut;
313
314 /**
315 * A reference to the PacketSorter.
316 */
317 private PacketSorter _packetSorter;
318
319 /**
320 * A reference to the DataHandler, if there is one
321 */
322 private TCPDataHandler _dataHandler;
323
324 /**
325 * The name of the Client we're connected to
326 */
327 private String _clientName;
328
329 /**
330 * The host list the Client has requested
331 */
332 private String _hostList;
333
334 //---STATIC ATTRIBUTES---
335
336 }