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.26
Committed: Sat May 18 18:16:01 2002 UTC (22 years ago) by tdb
Branch: MAIN
Changes since 1.25: +21 -2 lines
Log Message:
i-scream is now licensed under the GPL. I've added the GPL headers to every
source file, and put a full copy of the license in the appropriate places.
I think I've covered everything. This is going to be a mad commit ;)

File Contents

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