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

# Content
1 /*
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 //---PACKAGE DECLARATION---
21 package uk.org.iscream.cms.server.clientinterface;
22
23 //---IMPORTS---
24 import uk.org.iscream.cms.server.util.*;
25 import uk.org.iscream.cms.server.componentmanager.*;
26 import uk.org.iscream.cms.server.core.*;
27 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 * @author $Author: tdb $
41 * @version $Id: TCPControlHandler.java,v 1.25 2002/03/21 13:01:21 tdb Exp $
42 */
43 class TCPControlHandler extends Thread {
44
45 //---FINAL ATTRIBUTES---
46
47 /**
48 * The current CVS revision of this class
49 */
50 public final String REVISION = "$Revision: 1.25 $";
51
52 /**
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 public static final String PROTOVER = "PROTOCOL 1.1";
58
59 //---STATIC METHODS---
60
61 //---CONSTRUCTORS---
62
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 public TCPControlHandler(Socket socket, PacketSorter packetSorter) throws IOException {
71 // set the Thread name
72 setName("clientinterface.TCPControlHandler");
73
74 _socket = socket;
75 _packetSorter = packetSorter;
76 // setup the reader & writer
77 _socketIn = new BufferedReader(new InputStreamReader(_socket.getInputStream()));
78 _socketOut = new PrintWriter(_socket.getOutputStream(), true);
79 // set the default hostlist to "all host", for backward compatibility
80 _hostList = "";
81 _logger.write(toString(), Logger.SYSINIT, "created");
82 }
83
84 //---PUBLIC METHODS---
85
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 public void run() {
92 boolean run = true;
93 // Tell the client our protocol, and ask it for it's name
94 try {
95 send(PROTOVER);
96 _clientName = _socketIn.readLine();
97 if(_clientName==null) {
98 throw new IOException("Fatal error reading from client");
99 }
100 sendOK();
101 }
102 catch(IOException e) {
103 _logger.write(toString(), Logger.FATAL, "Fatal error, shutdown pending");
104 run=false;
105 }
106
107 _logger.write(toString(), Logger.DEBUG, "Client has connected: "+_clientName);
108
109 // loop until we decide to shutdown (run=false)
110 while(run) {
111 try {
112 String cmd = _socketIn.readLine();
113 if(cmd==null) {
114 throw new IOException("Fatal error reading from client");
115 }
116 // make a decision about what to do
117 if(cmd.equals("STARTCONFIG")) {
118 // get the configuration for this client
119 ConfigurationProxy cp = ConfigurationProxy.getInstance();
120 sendOK();
121 // get properties
122 cmd = _socketIn.readLine();
123 if(cmd==null) {
124 throw new IOException("Fatal error reading from client");
125 }
126 // provide all the requested properties
127 while(!cmd.equals("ENDCONFIG")) {
128 int midPoint = cmd.indexOf(";");
129 String config, property;
130 if(midPoint != -1) {
131 try {
132 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 }
154 catch (IndexOutOfBoundsException e) {
155 sendERROR();
156 }
157 }
158 else {
159 sendERROR();
160 }
161 cmd = _socketIn.readLine();
162 if(cmd==null) {
163 throw new IOException("Fatal error reading from client");
164 }
165 }
166 sendOK();
167 _logger.write(toString(), Logger.DEBUG, "Client has been configured");
168
169 }
170 else if(cmd.equals("STARTDATA")) {
171 // if we don't have a DataHandler, set one up
172 if(_dataHandler == null) {
173 // 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 // 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 // create a serversocket
188 // 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 // get the port
197 String port = String.valueOf(ss.getLocalPort());
198 // tell the client the port
199 send(port);
200 // wait for the client to connect
201 Socket s = ss.accept();
202 // when we get the Socket back, give it to the data thread
203 TCPDataHandler dh = new TCPDataHandler(s);
204 // register the DataHandler's queue, giving the host list
205 _packetSorter.register(dh.getQueue(), _hostList);
206 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 // start up the DataHandler
216 dh.start();
217 // Hold a reference to the DataHandler, so we can stop it later
218 _dataHandler = dh;
219 sendOK();
220 _logger.write(toString(), Logger.DEBUG, "Data stream started at Clients Request on port: "+port);
221 }
222 else {
223 sendERROR();
224 }
225 }
226 else if(cmd.equals("STOPDATA")) {
227 // attempt to close the data channel
228 if(closeData()) {
229 sendOK();
230 _logger.write(toString(), Logger.DEBUG, "Data stream stopped at Clients Request");
231 }
232 else {
233 sendERROR();
234 }
235 }
236 else if(cmd.equals("SETHOSTLIST")) {
237 // we can only set the host list when
238 // the DataHandler is not connected
239 if(_dataHandler == null) {
240 sendOK();
241 // read and set the host list
242 cmd = _socketIn.readLine();
243 if(cmd==null) {
244 throw new IOException("Fatal error reading from client");
245 }
246 _hostList = cmd;
247 sendOK();
248 }
249 else {
250 sendERROR();
251 }
252 }
253 else if(cmd.equals("DISCONNECT")) {
254 // we going to disconnect, so lets stop the main loop
255 run=false;
256 // if there is a DataHandler, we'd best shut it down
257 if(closeData()) {
258 _logger.write(toString(), Logger.DEBUG, "Data stream stopped at Clients Request");
259 }
260 // say bye to the client
261 sendOK();
262 // close the reader, writer and Socket
263 _socketIn.close();
264 _socketOut.close();
265 _socket.close();
266 _logger.write(toString(), Logger.DEBUG, "Closing at Clients Request");
267 }
268 else {
269 sendERROR();
270 }
271 }
272 catch(IOException e) {
273 // if we get an exception, the client has probably left, so we stop
274 run=false;
275 _logger.write(toString(), Logger.FATAL, "Fatal communication error, shutdown pending");
276 }
277 }
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 }
282 _logger.write(toString(), Logger.DEBUG, "Shutting down Control Handler, client has gone.");
283 }
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 * This uses the uk.org.iscream.cms.server.util.NameFormat class
290 * 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
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
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
390 /**
391 * A reference to the PacketSorter.
392 */
393 private PacketSorter _packetSorter;
394
395 /**
396 * A reference to the DataHandler, if there is one
397 */
398 private TCPDataHandler _dataHandler;
399
400 /**
401 * The name of the Client we're connected to
402 */
403 private String _clientName;
404
405 /**
406 * The host list the Client has requested
407 */
408 private String _hostList;
409
410 //---STATIC ATTRIBUTES---
411
412 }