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.27
Committed: Tue May 21 16:47:16 2002 UTC (22 years ago) by tdb
Branch: MAIN
Changes since 1.26: +3 -2 lines
Log Message:
Added URL to GPL headers.

File Contents

# User Rev Content
1 tdb 1.26 /*
2     * i-scream central monitoring system
3 tdb 1.27 * http://www.i-scream.org.uk
4 tdb 1.26 * Copyright (C) 2000-2002 i-scream
5     *
6     * This program is free software; you can redistribute it and/or
7     * modify it under the terms of the GNU General Public License
8     * as published by the Free Software Foundation; either version 2
9     * of the License, or (at your option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with this program; if not, write to the Free Software
18     * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19     */
20    
21 tdb 1.1 //---PACKAGE DECLARATION---
22 tdb 1.23 package uk.org.iscream.cms.server.clientinterface;
23 tdb 1.1
24     //---IMPORTS---
25 tdb 1.23 import uk.org.iscream.cms.server.util.*;
26     import uk.org.iscream.cms.server.componentmanager.*;
27     import uk.org.iscream.cms.server.core.*;
28 tdb 1.1 import java.net.Socket;
29     import java.net.ServerSocket;
30     import java.io.InputStream;
31     import java.io.OutputStream;
32     import java.io.IOException;
33     import java.io.BufferedReader;
34     import java.io.PrintWriter;
35     import java.io.InputStreamReader;
36    
37    
38     /**
39     * Acts as a Control Handler to a TCP based client.
40     *
41 tdb 1.24 * @author $Author: tdb $
42 tdb 1.27 * @version $Id: TCPControlHandler.java,v 1.26 2002/05/18 18:16:01 tdb Exp $
43 tdb 1.1 */
44     class TCPControlHandler extends Thread {
45    
46     //---FINAL ATTRIBUTES---
47    
48     /**
49     * The current CVS revision of this class
50     */
51 tdb 1.27 public final String REVISION = "$Revision: 1.26 $";
52 tdb 1.1
53 tdb 1.9 /**
54     * This is our protocol version. It is hardcoded to ensure
55     * that it doesn't get accidently changed. A change to the
56     * protocol would also require changing this classes code !
57     */
58 tdb 1.4 public static final String PROTOVER = "PROTOCOL 1.1";
59 tdb 1.1
60     //---STATIC METHODS---
61    
62     //---CONSTRUCTORS---
63 tdb 1.9
64     /**
65     * Construct a new TCPControlHandler, and setup the reader
66     * and writer for the new Socket.
67     *
68     * @param socket The Socket connected to the new Client
69     * @param packetSorter A reference to the PacketSorter in the component
70     */
71 tdb 1.19 public TCPControlHandler(Socket socket, PacketSorter packetSorter) throws IOException {
72 tdb 1.18 // set the Thread name
73     setName("clientinterface.TCPControlHandler");
74    
75 tdb 1.1 _socket = socket;
76 tdb 1.9 _packetSorter = packetSorter;
77     // setup the reader & writer
78 tdb 1.1 _socketIn = new BufferedReader(new InputStreamReader(_socket.getInputStream()));
79 tdb 1.13 _socketOut = new PrintWriter(_socket.getOutputStream(), true);
80 tdb 1.9 // set the default hostlist to "all host", for backward compatibility
81 tdb 1.4 _hostList = "";
82 tdb 1.1 _logger.write(toString(), Logger.SYSINIT, "created");
83     }
84    
85     //---PUBLIC METHODS---
86 tdb 1.9
87     /**
88     * This method initiates the thread, setting things up, and then
89     * reading commands from the Client. It handles setting up of the
90     * DataHandler, and clean shutting down.
91     */
92 tdb 1.1 public void run() {
93     boolean run = true;
94 tdb 1.9 // Tell the client our protocol, and ask it for it's name
95 tdb 1.1 try {
96 tdb 1.12 send(PROTOVER);
97 tdb 1.1 _clientName = _socketIn.readLine();
98 tdb 1.11 if(_clientName==null) {
99 tdb 1.10 throw new IOException("Fatal error reading from client");
100     }
101 tdb 1.12 sendOK();
102 tdb 1.1 }
103     catch(IOException e) {
104     _logger.write(toString(), Logger.FATAL, "Fatal error, shutdown pending");
105     run=false;
106     }
107    
108 tdb 1.12 _logger.write(toString(), Logger.DEBUG, "Client has connected: "+_clientName);
109 tdb 1.1
110 tdb 1.9 // loop until we decide to shutdown (run=false)
111 tdb 1.1 while(run) {
112     try {
113     String cmd = _socketIn.readLine();
114 tdb 1.10 if(cmd==null) {
115     throw new IOException("Fatal error reading from client");
116     }
117 tdb 1.9 // make a decision about what to do
118 tdb 1.1 if(cmd.equals("STARTCONFIG")) {
119 tdb 1.9 // get the configuration for this client
120 tdb 1.21 ConfigurationProxy cp = ConfigurationProxy.getInstance();
121 tdb 1.12 sendOK();
122 tdb 1.1 // get properties
123     cmd = _socketIn.readLine();
124 tdb 1.10 if(cmd==null) {
125     throw new IOException("Fatal error reading from client");
126     }
127 tdb 1.9 // provide all the requested properties
128 tdb 1.1 while(!cmd.equals("ENDCONFIG")) {
129 tdb 1.22 int midPoint = cmd.indexOf(";");
130     String config, property;
131     if(midPoint != -1) {
132 tdb 1.7 try {
133 tdb 1.22 config = cmd.substring(0, midPoint);
134     property = cmd.substring(midPoint+1, cmd.length());
135     if(!config.equals("") && !property.equals("")) {
136     // client is restricted to these properties and configs
137     if( (property.startsWith("Client.") || property.startsWith("Host."))
138     && (config.startsWith("Client.") || config.startsWith("Host.")) ) {
139     try {
140     String returnedProperty = cp.getProperty(config, property);
141     send(returnedProperty);
142     }
143     catch (PropertyNotFoundException e) {
144     sendERROR();
145     }
146     }
147     else {
148     sendERROR();
149     }
150     }
151     else {
152     sendERROR();
153     }
154 tdb 1.7 }
155 tdb 1.22 catch (IndexOutOfBoundsException e) {
156 tdb 1.12 sendERROR();
157 tdb 1.7 }
158     }
159     else {
160 tdb 1.12 sendERROR();
161 tdb 1.1 }
162     cmd = _socketIn.readLine();
163 tdb 1.10 if(cmd==null) {
164     throw new IOException("Fatal error reading from client");
165     }
166 tdb 1.1 }
167 tdb 1.12 sendOK();
168     _logger.write(toString(), Logger.DEBUG, "Client has been configured");
169 tdb 1.1
170     }
171     else if(cmd.equals("STARTDATA")) {
172 tdb 1.9 // if we don't have a DataHandler, set one up
173 tdb 1.1 if(_dataHandler == null) {
174 tdb 1.25 // This ACL isn't dynamic like the others, but is instead
175     // recreated each time a client connects. Maybe this ACL
176     // could instead be done to allow only the correct client
177     // to connect to the data channel?
178    
179 tdb 1.24 // get our ACL from the configuration
180     ACL acl = null;
181     try {
182     String stringACL = ConfigurationProxy.getInstance().getProperty("ClientInterface", "ClientInterface.TCPDataChannelACL");
183     acl = new ACL(stringACL);
184     }
185     catch(PropertyNotFoundException e) {
186     _logger.write(toString(), Logger.WARNING, "No ACL found for ClientInterface (data channel listener): " + e);
187     }
188 tdb 1.1 // create a serversocket
189 tdb 1.24 // use an ACLServerSocket if we have an ACL
190     ServerSocket ss;
191     if(acl != null) {
192     ss = new ACLServerSocket(acl, 0);
193     }
194     else {
195     ss = new ServerSocket(0);
196     }
197 tdb 1.1 // get the port
198 tdb 1.24 String port = String.valueOf(ss.getLocalPort());
199 tdb 1.9 // tell the client the port
200 tdb 1.12 send(port);
201 tdb 1.9 // wait for the client to connect
202 tdb 1.1 Socket s = ss.accept();
203     // when we get the Socket back, give it to the data thread
204 tdb 1.4 TCPDataHandler dh = new TCPDataHandler(s);
205 tdb 1.9 // register the DataHandler's queue, giving the host list
206     _packetSorter.register(dh.getQueue(), _hostList);
207 tdb 1.19 try {
208     // startup a monitor on the DataHandler's queue
209     ConfigurationProxy cp = ConfigurationProxy.getInstance();
210     int queueMonitorInterval = Integer.parseInt(cp.getProperty("ClientInterface", "Queue.MonitorInterval"));
211     String queueName = _name + " TCPHandler:"+_socket.getInetAddress().getHostName();
212     dh.getQueue().startMonitor(queueMonitorInterval*1000, _packetSorter.getQueue(), queueName);
213     } catch (PropertyNotFoundException e) {
214     _logger.write(toString(), Logger.WARNING, "failed to find queue monitor config, disabling. " + e);
215     }
216 tdb 1.9 // start up the DataHandler
217 tdb 1.2 dh.start();
218 tdb 1.9 // Hold a reference to the DataHandler, so we can stop it later
219 tdb 1.1 _dataHandler = dh;
220 tdb 1.12 sendOK();
221     _logger.write(toString(), Logger.DEBUG, "Data stream started at Clients Request on port: "+port);
222 tdb 1.1 }
223     else {
224 tdb 1.12 sendERROR();
225 tdb 1.1 }
226     }
227     else if(cmd.equals("STOPDATA")) {
228 tdb 1.12 // attempt to close the data channel
229     if(closeData()) {
230     sendOK();
231     _logger.write(toString(), Logger.DEBUG, "Data stream stopped at Clients Request");
232 tdb 1.1 }
233     else {
234 tdb 1.12 sendERROR();
235 tdb 1.1 }
236     }
237 tdb 1.4 else if(cmd.equals("SETHOSTLIST")) {
238 tdb 1.9 // we can only set the host list when
239     // the DataHandler is not connected
240 tdb 1.5 if(_dataHandler == null) {
241 tdb 1.12 sendOK();
242 tdb 1.9 // read and set the host list
243 tdb 1.5 cmd = _socketIn.readLine();
244 tdb 1.10 if(cmd==null) {
245     throw new IOException("Fatal error reading from client");
246     }
247 tdb 1.5 _hostList = cmd;
248 tdb 1.12 sendOK();
249 tdb 1.5 }
250     else {
251 tdb 1.12 sendERROR();
252 tdb 1.5 }
253 tdb 1.4 }
254 tdb 1.2 else if(cmd.equals("DISCONNECT")) {
255 tdb 1.12 // we going to disconnect, so lets stop the main loop
256 tdb 1.1 run=false;
257 tdb 1.9 // if there is a DataHandler, we'd best shut it down
258 tdb 1.12 if(closeData()) {
259     _logger.write(toString(), Logger.DEBUG, "Data stream stopped at Clients Request");
260 tdb 1.3 }
261 tdb 1.12 // say bye to the client
262     sendOK();
263 tdb 1.9 // close the reader, writer and Socket
264 tdb 1.1 _socketIn.close();
265     _socketOut.close();
266     _socket.close();
267 tdb 1.12 _logger.write(toString(), Logger.DEBUG, "Closing at Clients Request");
268 tdb 1.1 }
269     else {
270 tdb 1.12 sendERROR();
271 tdb 1.1 }
272     }
273     catch(IOException e) {
274 tdb 1.9 // if we get an exception, the client has probably left, so we stop
275 tdb 1.8 run=false;
276     _logger.write(toString(), Logger.FATAL, "Fatal communication error, shutdown pending");
277 tdb 1.1 }
278 tdb 1.17 }
279     // we'll close any DataHandlers here that shouldn't be open still
280     if(closeData()) {
281     _logger.write(toString(), Logger.DEBUG, "Data stream stopped due to fatal client error");
282 tdb 1.1 }
283 tdb 1.12 _logger.write(toString(), Logger.DEBUG, "Shutting down Control Handler, client has gone.");
284 tdb 1.1 }
285    
286     /**
287     * Overrides the {@link java.lang.Object#toString() Object.toString()}
288     * method to provide clean logging (every class should have this).
289     *
290 tdb 1.23 * This uses the uk.org.iscream.cms.server.util.NameFormat class
291 tdb 1.1 * to format the toString()
292     *
293     * @return the name of this class and its CVS revision
294     */
295     public String toString() {
296     return FormatName.getName(
297     _name,
298     getClass().getName(),
299     REVISION);
300     }
301    
302     //---PRIVATE METHODS---
303 tdb 1.12
304     /**
305     * Attempt to close down the DataHandler. This will return
306     * true if it managed to close it down, or false if there
307     * wasn't a DataHandler to close.
308     *
309     * @return whether the channel could be closed
310     */
311     private boolean closeData() {
312     // if we have a DataHandler, shut it down
313     if(_dataHandler != null) {
314     // Deregister the DataHandler, giving the host list
315     _packetSorter.deregister(_dataHandler.getQueue(), _hostList);
316     // Shut down the data handler
317     _dataHandler.shutdown();
318     // Destroy our reference, leaving it for the GC
319     _dataHandler = null;
320     return true;
321     }
322     else {
323     return false;
324     }
325     }
326    
327     /**
328     * Send an OK message to the outgoing Socket
329     */
330     private void sendOK() {
331     send("OK");
332     }
333    
334     /**
335     * Send an ERROR message to the outgoing Socket.
336     */
337     private void sendERROR() {
338     send("ERROR");
339     }
340    
341     /**
342     * Send any String message to the outgoing Socket.
343     *
344     * @param message The message/command to send.
345     */
346     private void send(String message) {
347     _socketOut.println(message);
348     }
349 tdb 1.1
350     //---ACCESSOR/MUTATOR METHODS---
351    
352     //---ATTRIBUTES---
353    
354     /**
355     * This is the friendly identifier of the
356     * component this class is running in.
357     * eg, a Filter may be called "filter1",
358     * If this class does not have an owning
359     * component, a name from the configuration
360     * can be placed here. This name could also
361     * be changed to null for utility classes.
362     */
363     private String _name = ClientInterfaceMain.NAME;
364    
365     /**
366     * This holds a reference to the
367     * system logger that is being used.
368     */
369     private Logger _logger = ReferenceManager.getInstance().getLogger();
370    
371     /**
372     * A reference to the Configuration Manager the system is using
373     */
374     private ConfigurationManager _configManager = ReferenceManager.getInstance().getCM();
375    
376     /**
377     * The socket we are talking on
378     */
379     private Socket _socket;
380    
381     /**
382     * A hook to the inbound data from the socket
383     */
384     private BufferedReader _socketIn;
385    
386     /**
387     * A hook to the outbound stream for the socket
388     */
389     private PrintWriter _socketOut;
390 tdb 1.4
391 tdb 1.1 /**
392 tdb 1.4 * A reference to the PacketSorter.
393     */
394 tdb 1.9 private PacketSorter _packetSorter;
395 tdb 1.1
396 tdb 1.9 /**
397     * A reference to the DataHandler, if there is one
398     */
399 tdb 1.1 private TCPDataHandler _dataHandler;
400    
401 tdb 1.9 /**
402     * The name of the Client we're connected to
403     */
404 tdb 1.1 private String _clientName;
405 tdb 1.9
406     /**
407     * The host list the Client has requested
408     */
409 tdb 1.4 private String _hostList;
410 tdb 1.1
411     //---STATIC ATTRIBUTES---
412    
413     }