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.24
Committed: Wed Mar 20 12:56:37 2002 UTC (22 years, 2 months ago) by tdb
Branch: MAIN
Changes since 1.23: +21 -5 lines
Log Message:
Added Access Control Lists to all parts of the server that listen for
either TCP or UDP connections.

File Contents

# User Rev Content
1 tdb 1.1 //---PACKAGE DECLARATION---
2 tdb 1.23 package uk.org.iscream.cms.server.clientinterface;
3 tdb 1.1
4     //---IMPORTS---
5 tdb 1.23 import uk.org.iscream.cms.server.util.*;
6     import uk.org.iscream.cms.server.componentmanager.*;
7     import uk.org.iscream.cms.server.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.24 * @author $Author: tdb $
22     * @version $Id: TCPControlHandler.java,v 1.23 2001/05/29 17:02:34 tdb 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.24 public final String REVISION = "$Revision: 1.23 $";
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 tdb 1.24 // get our ACL from the configuration
155     ACL acl = null;
156     try {
157     String stringACL = ConfigurationProxy.getInstance().getProperty("ClientInterface", "ClientInterface.TCPDataChannelACL");
158     acl = new ACL(stringACL);
159     }
160     catch(PropertyNotFoundException e) {
161     _logger.write(toString(), Logger.WARNING, "No ACL found for ClientInterface (data channel listener): " + e);
162     }
163 tdb 1.1 // create a serversocket
164 tdb 1.24 // use an ACLServerSocket if we have an ACL
165     ServerSocket ss;
166     if(acl != null) {
167     ss = new ACLServerSocket(acl, 0);
168     }
169     else {
170     ss = new ServerSocket(0);
171     }
172 tdb 1.1 // get the port
173 tdb 1.24 String port = String.valueOf(ss.getLocalPort());
174 tdb 1.9 // tell the client the port
175 tdb 1.12 send(port);
176 tdb 1.9 // wait for the client to connect
177 tdb 1.1 Socket s = ss.accept();
178     // when we get the Socket back, give it to the data thread
179 tdb 1.4 TCPDataHandler dh = new TCPDataHandler(s);
180 tdb 1.9 // register the DataHandler's queue, giving the host list
181     _packetSorter.register(dh.getQueue(), _hostList);
182 tdb 1.19 try {
183     // startup a monitor on the DataHandler's queue
184     ConfigurationProxy cp = ConfigurationProxy.getInstance();
185     int queueMonitorInterval = Integer.parseInt(cp.getProperty("ClientInterface", "Queue.MonitorInterval"));
186     String queueName = _name + " TCPHandler:"+_socket.getInetAddress().getHostName();
187     dh.getQueue().startMonitor(queueMonitorInterval*1000, _packetSorter.getQueue(), queueName);
188     } catch (PropertyNotFoundException e) {
189     _logger.write(toString(), Logger.WARNING, "failed to find queue monitor config, disabling. " + e);
190     }
191 tdb 1.9 // start up the DataHandler
192 tdb 1.2 dh.start();
193 tdb 1.9 // Hold a reference to the DataHandler, so we can stop it later
194 tdb 1.1 _dataHandler = dh;
195 tdb 1.12 sendOK();
196     _logger.write(toString(), Logger.DEBUG, "Data stream started at Clients Request on port: "+port);
197 tdb 1.1 }
198     else {
199 tdb 1.12 sendERROR();
200 tdb 1.1 }
201     }
202     else if(cmd.equals("STOPDATA")) {
203 tdb 1.12 // attempt to close the data channel
204     if(closeData()) {
205     sendOK();
206     _logger.write(toString(), Logger.DEBUG, "Data stream stopped at Clients Request");
207 tdb 1.1 }
208     else {
209 tdb 1.12 sendERROR();
210 tdb 1.1 }
211     }
212 tdb 1.4 else if(cmd.equals("SETHOSTLIST")) {
213 tdb 1.9 // we can only set the host list when
214     // the DataHandler is not connected
215 tdb 1.5 if(_dataHandler == null) {
216 tdb 1.12 sendOK();
217 tdb 1.9 // read and set the host list
218 tdb 1.5 cmd = _socketIn.readLine();
219 tdb 1.10 if(cmd==null) {
220     throw new IOException("Fatal error reading from client");
221     }
222 tdb 1.5 _hostList = cmd;
223 tdb 1.12 sendOK();
224 tdb 1.5 }
225     else {
226 tdb 1.12 sendERROR();
227 tdb 1.5 }
228 tdb 1.4 }
229 tdb 1.2 else if(cmd.equals("DISCONNECT")) {
230 tdb 1.12 // we going to disconnect, so lets stop the main loop
231 tdb 1.1 run=false;
232 tdb 1.9 // if there is a DataHandler, we'd best shut it down
233 tdb 1.12 if(closeData()) {
234     _logger.write(toString(), Logger.DEBUG, "Data stream stopped at Clients Request");
235 tdb 1.3 }
236 tdb 1.12 // say bye to the client
237     sendOK();
238 tdb 1.9 // close the reader, writer and Socket
239 tdb 1.1 _socketIn.close();
240     _socketOut.close();
241     _socket.close();
242 tdb 1.12 _logger.write(toString(), Logger.DEBUG, "Closing at Clients Request");
243 tdb 1.1 }
244     else {
245 tdb 1.12 sendERROR();
246 tdb 1.1 }
247     }
248     catch(IOException e) {
249 tdb 1.9 // if we get an exception, the client has probably left, so we stop
250 tdb 1.8 run=false;
251     _logger.write(toString(), Logger.FATAL, "Fatal communication error, shutdown pending");
252 tdb 1.1 }
253 tdb 1.17 }
254     // we'll close any DataHandlers here that shouldn't be open still
255     if(closeData()) {
256     _logger.write(toString(), Logger.DEBUG, "Data stream stopped due to fatal client error");
257 tdb 1.1 }
258 tdb 1.12 _logger.write(toString(), Logger.DEBUG, "Shutting down Control Handler, client has gone.");
259 tdb 1.1 }
260    
261     /**
262     * Overrides the {@link java.lang.Object#toString() Object.toString()}
263     * method to provide clean logging (every class should have this).
264     *
265 tdb 1.23 * This uses the uk.org.iscream.cms.server.util.NameFormat class
266 tdb 1.1 * to format the toString()
267     *
268     * @return the name of this class and its CVS revision
269     */
270     public String toString() {
271     return FormatName.getName(
272     _name,
273     getClass().getName(),
274     REVISION);
275     }
276    
277     //---PRIVATE METHODS---
278 tdb 1.12
279     /**
280     * Attempt to close down the DataHandler. This will return
281     * true if it managed to close it down, or false if there
282     * wasn't a DataHandler to close.
283     *
284     * @return whether the channel could be closed
285     */
286     private boolean closeData() {
287     // if we have a DataHandler, shut it down
288     if(_dataHandler != null) {
289     // Deregister the DataHandler, giving the host list
290     _packetSorter.deregister(_dataHandler.getQueue(), _hostList);
291     // Shut down the data handler
292     _dataHandler.shutdown();
293     // Destroy our reference, leaving it for the GC
294     _dataHandler = null;
295     return true;
296     }
297     else {
298     return false;
299     }
300     }
301    
302     /**
303     * Send an OK message to the outgoing Socket
304     */
305     private void sendOK() {
306     send("OK");
307     }
308    
309     /**
310     * Send an ERROR message to the outgoing Socket.
311     */
312     private void sendERROR() {
313     send("ERROR");
314     }
315    
316     /**
317     * Send any String message to the outgoing Socket.
318     *
319     * @param message The message/command to send.
320     */
321     private void send(String message) {
322     _socketOut.println(message);
323     }
324 tdb 1.1
325     //---ACCESSOR/MUTATOR METHODS---
326    
327     //---ATTRIBUTES---
328    
329     /**
330     * This is the friendly identifier of the
331     * component this class is running in.
332     * eg, a Filter may be called "filter1",
333     * If this class does not have an owning
334     * component, a name from the configuration
335     * can be placed here. This name could also
336     * be changed to null for utility classes.
337     */
338     private String _name = ClientInterfaceMain.NAME;
339    
340     /**
341     * This holds a reference to the
342     * system logger that is being used.
343     */
344     private Logger _logger = ReferenceManager.getInstance().getLogger();
345    
346     /**
347     * A reference to the Configuration Manager the system is using
348     */
349     private ConfigurationManager _configManager = ReferenceManager.getInstance().getCM();
350    
351     /**
352     * The socket we are talking on
353     */
354     private Socket _socket;
355    
356     /**
357     * A hook to the inbound data from the socket
358     */
359     private BufferedReader _socketIn;
360    
361     /**
362     * A hook to the outbound stream for the socket
363     */
364     private PrintWriter _socketOut;
365 tdb 1.4
366 tdb 1.1 /**
367 tdb 1.4 * A reference to the PacketSorter.
368     */
369 tdb 1.9 private PacketSorter _packetSorter;
370 tdb 1.1
371 tdb 1.9 /**
372     * A reference to the DataHandler, if there is one
373     */
374 tdb 1.1 private TCPDataHandler _dataHandler;
375    
376 tdb 1.9 /**
377     * The name of the Client we're connected to
378     */
379 tdb 1.1 private String _clientName;
380 tdb 1.9
381     /**
382     * The host list the Client has requested
383     */
384 tdb 1.4 private String _hostList;
385 tdb 1.1
386     //---STATIC ATTRIBUTES---
387    
388     }