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.23
Committed: Tue May 29 17:02:34 2001 UTC (22 years, 11 months ago) by tdb
Branch: MAIN
Branch point for: SERVER_PIRCBOT
Changes since 1.22: +7 -7 lines
Log Message:
Major change in the java package naming. This has been held off for some time
now, but it really needed doing. The future packaging of all i-scream products
will be;

uk.org.iscream.<product>.<subpart>.*

In the case of the central monitoring system server this will be;

uk.org.iscream.cms.server.*

The whole server has been changed to follow this structure, and tested to a
smallish extent. Further changes in other parts of the CMS will follow.

File Contents

# Content
1 //---PACKAGE DECLARATION---
2 package uk.org.iscream.cms.server.clientinterface;
3
4 //---IMPORTS---
5 import uk.org.iscream.cms.server.util.*;
6 import uk.org.iscream.cms.server.componentmanager.*;
7 import uk.org.iscream.cms.server.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.22 2001/03/19 02:10:53 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.22 $";
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 // set the Thread name
53 setName("clientinterface.TCPControlHandler");
54
55 _socket = socket;
56 _packetSorter = packetSorter;
57 // setup the reader & writer
58 _socketIn = new BufferedReader(new InputStreamReader(_socket.getInputStream()));
59 _socketOut = new PrintWriter(_socket.getOutputStream(), true);
60 // set the default hostlist to "all host", for backward compatibility
61 _hostList = "";
62 _logger.write(toString(), Logger.SYSINIT, "created");
63 }
64
65 //---PUBLIC METHODS---
66
67 /**
68 * This method initiates the thread, setting things up, and then
69 * reading commands from the Client. It handles setting up of the
70 * DataHandler, and clean shutting down.
71 */
72 public void run() {
73 boolean run = true;
74 // Tell the client our protocol, and ask it for it's name
75 try {
76 send(PROTOVER);
77 _clientName = _socketIn.readLine();
78 if(_clientName==null) {
79 throw new IOException("Fatal error reading from client");
80 }
81 sendOK();
82 }
83 catch(IOException e) {
84 _logger.write(toString(), Logger.FATAL, "Fatal error, shutdown pending");
85 run=false;
86 }
87
88 _logger.write(toString(), Logger.DEBUG, "Client has connected: "+_clientName);
89
90 // loop until we decide to shutdown (run=false)
91 while(run) {
92 try {
93 String cmd = _socketIn.readLine();
94 if(cmd==null) {
95 throw new IOException("Fatal error reading from client");
96 }
97 // make a decision about what to do
98 if(cmd.equals("STARTCONFIG")) {
99 // get the configuration for this client
100 ConfigurationProxy cp = ConfigurationProxy.getInstance();
101 sendOK();
102 // get properties
103 cmd = _socketIn.readLine();
104 if(cmd==null) {
105 throw new IOException("Fatal error reading from client");
106 }
107 // provide all the requested properties
108 while(!cmd.equals("ENDCONFIG")) {
109 int midPoint = cmd.indexOf(";");
110 String config, property;
111 if(midPoint != -1) {
112 try {
113 config = cmd.substring(0, midPoint);
114 property = cmd.substring(midPoint+1, cmd.length());
115 if(!config.equals("") && !property.equals("")) {
116 // client is restricted to these properties and configs
117 if( (property.startsWith("Client.") || property.startsWith("Host."))
118 && (config.startsWith("Client.") || config.startsWith("Host.")) ) {
119 try {
120 String returnedProperty = cp.getProperty(config, property);
121 send(returnedProperty);
122 }
123 catch (PropertyNotFoundException e) {
124 sendERROR();
125 }
126 }
127 else {
128 sendERROR();
129 }
130 }
131 else {
132 sendERROR();
133 }
134 }
135 catch (IndexOutOfBoundsException e) {
136 sendERROR();
137 }
138 }
139 else {
140 sendERROR();
141 }
142 cmd = _socketIn.readLine();
143 if(cmd==null) {
144 throw new IOException("Fatal error reading from client");
145 }
146 }
147 sendOK();
148 _logger.write(toString(), Logger.DEBUG, "Client has been configured");
149
150 }
151 else if(cmd.equals("STARTDATA")) {
152 // if we don't have a DataHandler, set one up
153 if(_dataHandler == null) {
154 // create a serversocket
155 ServerSocket ss = new ServerSocket(0);
156 // get the port
157 String port = new Integer(ss.getLocalPort()).toString();
158 // tell the client the port
159 send(port);
160 // wait for the client to connect
161 Socket s = ss.accept();
162 // when we get the Socket back, give it to the data thread
163 TCPDataHandler dh = new TCPDataHandler(s);
164 // register the DataHandler's queue, giving the host list
165 _packetSorter.register(dh.getQueue(), _hostList);
166 try {
167 // startup a monitor on the DataHandler's queue
168 ConfigurationProxy cp = ConfigurationProxy.getInstance();
169 int queueMonitorInterval = Integer.parseInt(cp.getProperty("ClientInterface", "Queue.MonitorInterval"));
170 String queueName = _name + " TCPHandler:"+_socket.getInetAddress().getHostName();
171 dh.getQueue().startMonitor(queueMonitorInterval*1000, _packetSorter.getQueue(), queueName);
172 } catch (PropertyNotFoundException e) {
173 _logger.write(toString(), Logger.WARNING, "failed to find queue monitor config, disabling. " + e);
174 }
175 // start up the DataHandler
176 dh.start();
177 // Hold a reference to the DataHandler, so we can stop it later
178 _dataHandler = dh;
179 sendOK();
180 _logger.write(toString(), Logger.DEBUG, "Data stream started at Clients Request on port: "+port);
181 }
182 else {
183 sendERROR();
184 }
185 }
186 else if(cmd.equals("STOPDATA")) {
187 // attempt to close the data channel
188 if(closeData()) {
189 sendOK();
190 _logger.write(toString(), Logger.DEBUG, "Data stream stopped at Clients Request");
191 }
192 else {
193 sendERROR();
194 }
195 }
196 else if(cmd.equals("SETHOSTLIST")) {
197 // we can only set the host list when
198 // the DataHandler is not connected
199 if(_dataHandler == null) {
200 sendOK();
201 // read and set the host list
202 cmd = _socketIn.readLine();
203 if(cmd==null) {
204 throw new IOException("Fatal error reading from client");
205 }
206 _hostList = cmd;
207 sendOK();
208 }
209 else {
210 sendERROR();
211 }
212 }
213 else if(cmd.equals("DISCONNECT")) {
214 // we going to disconnect, so lets stop the main loop
215 run=false;
216 // if there is a DataHandler, we'd best shut it down
217 if(closeData()) {
218 _logger.write(toString(), Logger.DEBUG, "Data stream stopped at Clients Request");
219 }
220 // say bye to the client
221 sendOK();
222 // close the reader, writer and Socket
223 _socketIn.close();
224 _socketOut.close();
225 _socket.close();
226 _logger.write(toString(), Logger.DEBUG, "Closing at Clients Request");
227 }
228 else {
229 sendERROR();
230 }
231 }
232 catch(IOException e) {
233 // if we get an exception, the client has probably left, so we stop
234 run=false;
235 _logger.write(toString(), Logger.FATAL, "Fatal communication error, shutdown pending");
236 }
237 }
238 // we'll close any DataHandlers here that shouldn't be open still
239 if(closeData()) {
240 _logger.write(toString(), Logger.DEBUG, "Data stream stopped due to fatal client error");
241 }
242 _logger.write(toString(), Logger.DEBUG, "Shutting down Control Handler, client has gone.");
243 }
244
245 /**
246 * Overrides the {@link java.lang.Object#toString() Object.toString()}
247 * method to provide clean logging (every class should have this).
248 *
249 * This uses the uk.org.iscream.cms.server.util.NameFormat class
250 * to format the toString()
251 *
252 * @return the name of this class and its CVS revision
253 */
254 public String toString() {
255 return FormatName.getName(
256 _name,
257 getClass().getName(),
258 REVISION);
259 }
260
261 //---PRIVATE METHODS---
262
263 /**
264 * Attempt to close down the DataHandler. This will return
265 * true if it managed to close it down, or false if there
266 * wasn't a DataHandler to close.
267 *
268 * @return whether the channel could be closed
269 */
270 private boolean closeData() {
271 // if we have a DataHandler, shut it down
272 if(_dataHandler != null) {
273 // Deregister the DataHandler, giving the host list
274 _packetSorter.deregister(_dataHandler.getQueue(), _hostList);
275 // Shut down the data handler
276 _dataHandler.shutdown();
277 // Destroy our reference, leaving it for the GC
278 _dataHandler = null;
279 return true;
280 }
281 else {
282 return false;
283 }
284 }
285
286 /**
287 * Send an OK message to the outgoing Socket
288 */
289 private void sendOK() {
290 send("OK");
291 }
292
293 /**
294 * Send an ERROR message to the outgoing Socket.
295 */
296 private void sendERROR() {
297 send("ERROR");
298 }
299
300 /**
301 * Send any String message to the outgoing Socket.
302 *
303 * @param message The message/command to send.
304 */
305 private void send(String message) {
306 _socketOut.println(message);
307 }
308
309 //---ACCESSOR/MUTATOR METHODS---
310
311 //---ATTRIBUTES---
312
313 /**
314 * This is the friendly identifier of the
315 * component this class is running in.
316 * eg, a Filter may be called "filter1",
317 * If this class does not have an owning
318 * component, a name from the configuration
319 * can be placed here. This name could also
320 * be changed to null for utility classes.
321 */
322 private String _name = ClientInterfaceMain.NAME;
323
324 /**
325 * This holds a reference to the
326 * system logger that is being used.
327 */
328 private Logger _logger = ReferenceManager.getInstance().getLogger();
329
330 /**
331 * A reference to the Configuration Manager the system is using
332 */
333 private ConfigurationManager _configManager = ReferenceManager.getInstance().getCM();
334
335 /**
336 * The socket we are talking on
337 */
338 private Socket _socket;
339
340 /**
341 * A hook to the inbound data from the socket
342 */
343 private BufferedReader _socketIn;
344
345 /**
346 * A hook to the outbound stream for the socket
347 */
348 private PrintWriter _socketOut;
349
350 /**
351 * A reference to the PacketSorter.
352 */
353 private PacketSorter _packetSorter;
354
355 /**
356 * A reference to the DataHandler, if there is one
357 */
358 private TCPDataHandler _dataHandler;
359
360 /**
361 * The name of the Client we're connected to
362 */
363 private String _clientName;
364
365 /**
366 * The host list the Client has requested
367 */
368 private String _hostList;
369
370 //---STATIC ATTRIBUTES---
371
372 }