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

# 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: tdb $
22 * @version $Id: TCPControlHandler.java,v 1.23 2001/05/29 17:02:34 tdb 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.23 $";
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 // 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 // create a serversocket
164 // 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 // get the port
173 String port = String.valueOf(ss.getLocalPort());
174 // tell the client the port
175 send(port);
176 // wait for the client to connect
177 Socket s = ss.accept();
178 // when we get the Socket back, give it to the data thread
179 TCPDataHandler dh = new TCPDataHandler(s);
180 // register the DataHandler's queue, giving the host list
181 _packetSorter.register(dh.getQueue(), _hostList);
182 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 // start up the DataHandler
192 dh.start();
193 // Hold a reference to the DataHandler, so we can stop it later
194 _dataHandler = dh;
195 sendOK();
196 _logger.write(toString(), Logger.DEBUG, "Data stream started at Clients Request on port: "+port);
197 }
198 else {
199 sendERROR();
200 }
201 }
202 else if(cmd.equals("STOPDATA")) {
203 // attempt to close the data channel
204 if(closeData()) {
205 sendOK();
206 _logger.write(toString(), Logger.DEBUG, "Data stream stopped at Clients Request");
207 }
208 else {
209 sendERROR();
210 }
211 }
212 else if(cmd.equals("SETHOSTLIST")) {
213 // we can only set the host list when
214 // the DataHandler is not connected
215 if(_dataHandler == null) {
216 sendOK();
217 // read and set the host list
218 cmd = _socketIn.readLine();
219 if(cmd==null) {
220 throw new IOException("Fatal error reading from client");
221 }
222 _hostList = cmd;
223 sendOK();
224 }
225 else {
226 sendERROR();
227 }
228 }
229 else if(cmd.equals("DISCONNECT")) {
230 // we going to disconnect, so lets stop the main loop
231 run=false;
232 // if there is a DataHandler, we'd best shut it down
233 if(closeData()) {
234 _logger.write(toString(), Logger.DEBUG, "Data stream stopped at Clients Request");
235 }
236 // say bye to the client
237 sendOK();
238 // close the reader, writer and Socket
239 _socketIn.close();
240 _socketOut.close();
241 _socket.close();
242 _logger.write(toString(), Logger.DEBUG, "Closing at Clients Request");
243 }
244 else {
245 sendERROR();
246 }
247 }
248 catch(IOException e) {
249 // if we get an exception, the client has probably left, so we stop
250 run=false;
251 _logger.write(toString(), Logger.FATAL, "Fatal communication error, shutdown pending");
252 }
253 }
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 }
258 _logger.write(toString(), Logger.DEBUG, "Shutting down Control Handler, client has gone.");
259 }
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 * This uses the uk.org.iscream.cms.server.util.NameFormat class
266 * 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
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
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
366 /**
367 * A reference to the PacketSorter.
368 */
369 private PacketSorter _packetSorter;
370
371 /**
372 * A reference to the DataHandler, if there is one
373 */
374 private TCPDataHandler _dataHandler;
375
376 /**
377 * The name of the Client we're connected to
378 */
379 private String _clientName;
380
381 /**
382 * The host list the Client has requested
383 */
384 private String _hostList;
385
386 //---STATIC ATTRIBUTES---
387
388 }