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.22
Committed: Mon Mar 19 02:10:53 2001 UTC (23 years, 2 months ago) by tdb
Branch: MAIN
CVS Tags: PROJECT_COMPLETION
Changes since 1.21: +27 -7 lines
Log Message:
Gives the client a more versatile range of configuration. Restrictions still
apply. See protocol update for more details.

File Contents

# User Rev Content
1 tdb 1.1 //---PACKAGE DECLARATION---
2 tdb 1.20 package uk.org.iscream.clientinterface;
3 tdb 1.1
4     //---IMPORTS---
5 tdb 1.20 import uk.org.iscream.util.*;
6     import uk.org.iscream.componentmanager.*;
7     import uk.org.iscream.core.*;
8 tdb 1.1 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 tdb 1.2 * @author $Author: tdb1 $
22 tdb 1.22 * @version $Id: TCPControlHandler.java,v 1.21 2001/03/17 03:57:38 tdb1 Exp $
23 tdb 1.1 */
24     class TCPControlHandler extends Thread {
25    
26     //---FINAL ATTRIBUTES---
27    
28     /**
29     * The current CVS revision of this class
30     */
31 tdb 1.22 public final String REVISION = "$Revision: 1.21 $";
32 tdb 1.1
33 tdb 1.9 /**
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 tdb 1.4 public static final String PROTOVER = "PROTOCOL 1.1";
39 tdb 1.1
40     //---STATIC METHODS---
41    
42     //---CONSTRUCTORS---
43 tdb 1.9
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 tdb 1.19 public TCPControlHandler(Socket socket, PacketSorter packetSorter) throws IOException {
52 tdb 1.18 // set the Thread name
53     setName("clientinterface.TCPControlHandler");
54    
55 tdb 1.1 _socket = socket;
56 tdb 1.9 _packetSorter = packetSorter;
57     // setup the reader & writer
58 tdb 1.1 _socketIn = new BufferedReader(new InputStreamReader(_socket.getInputStream()));
59 tdb 1.13 _socketOut = new PrintWriter(_socket.getOutputStream(), true);
60 tdb 1.9 // set the default hostlist to "all host", for backward compatibility
61 tdb 1.4 _hostList = "";
62 tdb 1.1 _logger.write(toString(), Logger.SYSINIT, "created");
63     }
64    
65     //---PUBLIC METHODS---
66 tdb 1.9
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 tdb 1.1 public void run() {
73     boolean run = true;
74 tdb 1.9 // Tell the client our protocol, and ask it for it's name
75 tdb 1.1 try {
76 tdb 1.12 send(PROTOVER);
77 tdb 1.1 _clientName = _socketIn.readLine();
78 tdb 1.11 if(_clientName==null) {
79 tdb 1.10 throw new IOException("Fatal error reading from client");
80     }
81 tdb 1.12 sendOK();
82 tdb 1.1 }
83     catch(IOException e) {
84     _logger.write(toString(), Logger.FATAL, "Fatal error, shutdown pending");
85     run=false;
86     }
87    
88 tdb 1.12 _logger.write(toString(), Logger.DEBUG, "Client has connected: "+_clientName);
89 tdb 1.1
90 tdb 1.9 // loop until we decide to shutdown (run=false)
91 tdb 1.1 while(run) {
92     try {
93     String cmd = _socketIn.readLine();
94 tdb 1.10 if(cmd==null) {
95     throw new IOException("Fatal error reading from client");
96     }
97 tdb 1.9 // make a decision about what to do
98 tdb 1.1 if(cmd.equals("STARTCONFIG")) {
99 tdb 1.9 // get the configuration for this client
100 tdb 1.21 ConfigurationProxy cp = ConfigurationProxy.getInstance();
101 tdb 1.12 sendOK();
102 tdb 1.1 // get properties
103     cmd = _socketIn.readLine();
104 tdb 1.10 if(cmd==null) {
105     throw new IOException("Fatal error reading from client");
106     }
107 tdb 1.9 // provide all the requested properties
108 tdb 1.1 while(!cmd.equals("ENDCONFIG")) {
109 tdb 1.22 int midPoint = cmd.indexOf(";");
110     String config, property;
111     if(midPoint != -1) {
112 tdb 1.7 try {
113 tdb 1.22 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 tdb 1.7 }
135 tdb 1.22 catch (IndexOutOfBoundsException e) {
136 tdb 1.12 sendERROR();
137 tdb 1.7 }
138     }
139     else {
140 tdb 1.12 sendERROR();
141 tdb 1.1 }
142     cmd = _socketIn.readLine();
143 tdb 1.10 if(cmd==null) {
144     throw new IOException("Fatal error reading from client");
145     }
146 tdb 1.1 }
147 tdb 1.12 sendOK();
148     _logger.write(toString(), Logger.DEBUG, "Client has been configured");
149 tdb 1.1
150     }
151     else if(cmd.equals("STARTDATA")) {
152 tdb 1.9 // if we don't have a DataHandler, set one up
153 tdb 1.1 if(_dataHandler == null) {
154     // create a serversocket
155     ServerSocket ss = new ServerSocket(0);
156     // get the port
157 tdb 1.12 String port = new Integer(ss.getLocalPort()).toString();
158 tdb 1.9 // tell the client the port
159 tdb 1.12 send(port);
160 tdb 1.9 // wait for the client to connect
161 tdb 1.1 Socket s = ss.accept();
162     // when we get the Socket back, give it to the data thread
163 tdb 1.4 TCPDataHandler dh = new TCPDataHandler(s);
164 tdb 1.9 // register the DataHandler's queue, giving the host list
165     _packetSorter.register(dh.getQueue(), _hostList);
166 tdb 1.19 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 tdb 1.9 // start up the DataHandler
176 tdb 1.2 dh.start();
177 tdb 1.9 // Hold a reference to the DataHandler, so we can stop it later
178 tdb 1.1 _dataHandler = dh;
179 tdb 1.12 sendOK();
180     _logger.write(toString(), Logger.DEBUG, "Data stream started at Clients Request on port: "+port);
181 tdb 1.1 }
182     else {
183 tdb 1.12 sendERROR();
184 tdb 1.1 }
185     }
186     else if(cmd.equals("STOPDATA")) {
187 tdb 1.12 // attempt to close the data channel
188     if(closeData()) {
189     sendOK();
190     _logger.write(toString(), Logger.DEBUG, "Data stream stopped at Clients Request");
191 tdb 1.1 }
192     else {
193 tdb 1.12 sendERROR();
194 tdb 1.1 }
195     }
196 tdb 1.4 else if(cmd.equals("SETHOSTLIST")) {
197 tdb 1.9 // we can only set the host list when
198     // the DataHandler is not connected
199 tdb 1.5 if(_dataHandler == null) {
200 tdb 1.12 sendOK();
201 tdb 1.9 // read and set the host list
202 tdb 1.5 cmd = _socketIn.readLine();
203 tdb 1.10 if(cmd==null) {
204     throw new IOException("Fatal error reading from client");
205     }
206 tdb 1.5 _hostList = cmd;
207 tdb 1.12 sendOK();
208 tdb 1.5 }
209     else {
210 tdb 1.12 sendERROR();
211 tdb 1.5 }
212 tdb 1.4 }
213 tdb 1.2 else if(cmd.equals("DISCONNECT")) {
214 tdb 1.12 // we going to disconnect, so lets stop the main loop
215 tdb 1.1 run=false;
216 tdb 1.9 // if there is a DataHandler, we'd best shut it down
217 tdb 1.12 if(closeData()) {
218     _logger.write(toString(), Logger.DEBUG, "Data stream stopped at Clients Request");
219 tdb 1.3 }
220 tdb 1.12 // say bye to the client
221     sendOK();
222 tdb 1.9 // close the reader, writer and Socket
223 tdb 1.1 _socketIn.close();
224     _socketOut.close();
225     _socket.close();
226 tdb 1.12 _logger.write(toString(), Logger.DEBUG, "Closing at Clients Request");
227 tdb 1.1 }
228     else {
229 tdb 1.12 sendERROR();
230 tdb 1.1 }
231     }
232     catch(IOException e) {
233 tdb 1.9 // if we get an exception, the client has probably left, so we stop
234 tdb 1.8 run=false;
235     _logger.write(toString(), Logger.FATAL, "Fatal communication error, shutdown pending");
236 tdb 1.1 }
237 tdb 1.17 }
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 tdb 1.1 }
242 tdb 1.12 _logger.write(toString(), Logger.DEBUG, "Shutting down Control Handler, client has gone.");
243 tdb 1.1 }
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 tdb 1.20 * This uses the uk.org.iscream.util.NameFormat class
250 tdb 1.1 * 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 tdb 1.12
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 tdb 1.1
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 tdb 1.4
350 tdb 1.1 /**
351 tdb 1.4 * A reference to the PacketSorter.
352     */
353 tdb 1.9 private PacketSorter _packetSorter;
354 tdb 1.1
355 tdb 1.9 /**
356     * A reference to the DataHandler, if there is one
357     */
358 tdb 1.1 private TCPDataHandler _dataHandler;
359    
360 tdb 1.9 /**
361     * The name of the Client we're connected to
362     */
363 tdb 1.1 private String _clientName;
364 tdb 1.9
365     /**
366     * The host list the Client has requested
367     */
368 tdb 1.4 private String _hostList;
369 tdb 1.1
370     //---STATIC ATTRIBUTES---
371    
372     }