ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/cms/source/server/uk/org/iscream/cms/server/client/WebFeeder.java
Revision: 1.26
Committed: Sun Sep 25 09:57:40 2005 UTC (18 years, 7 months ago) by tdb
Branch: MAIN
CVS Tags: HEAD
Changes since 1.25: +4 -3 lines
Error occurred while calculating annotation data.
Log Message:
Fix compile problems on j2se 1.5 - our Queue class conflicted with one in
java.util. Also fix an API issue when running the server on Windows - the
println method sends '\r\n' on Windows instead of '\n' on Unix, which
confuses applications such as ihost.

Patch provided by: skel

File Contents

# Content
1 /*
2 * i-scream central monitoring system
3 * http://www.i-scream.org
4 * 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 //---PACKAGE DECLARATION---
22 package uk.org.iscream.cms.server.client;
23
24 //---IMPORTS---
25 import uk.org.iscream.cms.server.componentmanager.*;
26 import uk.org.iscream.cms.server.core.*;
27 import uk.org.iscream.cms.util.*;
28 import java.io.*;
29
30 /**
31 * Provides a feed to the webpage system.
32 *
33 * @author $Author: tdb $
34 * @version $Id: WebFeeder.java,v 1.25 2004/08/01 10:40:41 tdb Exp $
35 */
36 public class WebFeeder extends Thread {
37
38 //---FINAL ATTRIBUTES---
39
40 /**
41 * The current CVS revision of this class
42 */
43 public static final String REVISION = "$Revision: 1.25 $";
44
45 /**
46 * Default check period in seconds (30 minutes)
47 */
48 public final int DEFAULT_CHECK_PERIOD = 1800;
49
50 /**
51 * Delete alerts older than this in seconds, default.
52 */
53 public final int DEFAULT_AGE = 3600;
54
55 /**
56 * The default path seperator, here for convienience
57 */
58 private final String sep = File.separator;
59
60 //---STATIC METHODS---
61
62 /**
63 * Return a reference to the single class.
64 * Construct it if it does not already exist, otherwise just return the reference.
65 */
66 public synchronized static WebFeeder getInstance() {
67 if (_instance == null){
68 _instance = new WebFeeder();
69 }
70 return _instance;
71 }
72
73 //---CONSTRUCTORS---
74
75 /**
76 * Construct a new WebFeeder. This will also wipe out any
77 * old Alerts, as these can't be carried from one session
78 * until the next.
79 */
80 private WebFeeder() {
81 // do something, or nothing.. but must be private
82 // don't need to cleanup latest data
83
84 // -- cleanup old alerts
85 // get config proxy
86 ConfigurationProxy cp = ConfigurationProxy.getInstance();
87 // get file locations
88 String rootPath, alertSubDir, alertFileName;
89 try {
90 // work out where things are
91 rootPath = cp.getProperty("WebFeeder", "WebFeeder.rootPath");
92 alertSubDir = cp.getProperty("WebFeeder", "WebFeeder.alertSubDir");
93 File alertsDir = new File(rootPath, alertSubDir);
94 if(deleteContents(alertsDir)) {
95 _logger.write(this.toString(), Logger.DEBUG, "Deleted all files and directories from: "+rootPath+sep+alertSubDir);
96 } else {
97 _logger.write(this.toString(), Logger.WARNING, "Failed to delete all files and directories from: "+rootPath+sep+alertSubDir);
98 }
99 // cleanup complete
100 } catch (PropertyNotFoundException e) {
101 _logger.write(this.toString(), Logger.ERROR, "Failed to cleanup on construction, due to failing to get config for Alert Data: "+e);
102 // just leave it at that
103 }
104
105 // set our name and startup
106 setName("client.WebFeeder");
107 start();
108 }
109
110 //---PUBLIC METHODS---
111
112 /**
113 * Thread loop, will check at intervals for any files
114 * that need to be "cleaned up". This will normally
115 * be OK and FINAL alerts that have been around for longer
116 * than a specified period of time.
117 */
118 public void run() {
119 boolean running = true;
120 // get config proxy
121 ConfigurationProxy cp = ConfigurationProxy.getInstance();
122 // loop round
123 while(running) {
124 // get our check period
125 int checkPeriod = 0;
126 try {
127 checkPeriod = Integer.parseInt(cp.getProperty("WebFeeder", "WebFeeder.checkPeriod"));
128 } catch (NumberFormatException e) {
129 checkPeriod = DEFAULT_CHECK_PERIOD;
130 _logger.write(toString(), Logger.WARNING, "Erronous WebFeeder.checkPeriod value in configuration using default of " + checkPeriod + " seconds");
131 } catch (PropertyNotFoundException e) {
132 checkPeriod = DEFAULT_CHECK_PERIOD;
133 _logger.write(toString(), Logger.WARNING, "WebFeeder.checkPeriod value unavailable using default of " + checkPeriod + " seconds");
134 }
135 // wait for the check period
136 try {
137 Thread.sleep(checkPeriod * 1000);
138 } catch (InterruptedException e) {
139 }
140
141 // get alerts directory
142 String rootPath, alertSubDir, alertFileName;
143 try {
144 rootPath = cp.getProperty("WebFeeder", "WebFeeder.rootPath");
145 alertSubDir = cp.getProperty("WebFeeder", "WebFeeder.alertSubDir");
146 alertFileName = cp.getProperty("WebFeeder", "WebFeeder.alertFileName");
147 } catch (PropertyNotFoundException e) {
148 _logger.write(this.toString(), Logger.ERROR, "Failed to get config for Alert Data: "+e);
149 // bail out
150 continue;
151 }
152
153 // get the "age" barrier
154 int deleteOlderThan = 0;
155 try {
156 deleteOlderThan = Integer.parseInt(cp.getProperty("WebFeeder", "WebFeeder.alertDeleteOlderThan"));
157 } catch (NumberFormatException e) {
158 deleteOlderThan = DEFAULT_AGE;
159 _logger.write(toString(), Logger.WARNING, "Erronous WebFeeder.alertDeleteOlderThan value in configuration using default of " + deleteOlderThan + " seconds");
160 } catch (PropertyNotFoundException e) {
161 deleteOlderThan = DEFAULT_AGE;
162 _logger.write(toString(), Logger.WARNING, "WebFeeder.alertDeleteOlderThan value unavailable using default of " + deleteOlderThan + " seconds");
163 }
164
165 // list the files and delete as appropriate
166 File alertsDir = new File(rootPath, alertSubDir);
167 // check it's a directory
168 if(alertsDir.isDirectory()) {
169 // get all the hostnames directories
170 File[] contents = alertsDir.listFiles();
171 for(int i=0; i < contents.length; i++) {
172 // get a single directory from the array..
173 File hostdir = contents[i];
174 // ..and check it's a directory
175 if(hostdir.isDirectory()) {
176 // if this is set, we clean files older than it
177 long deleteFiles = -1;
178 // get all the contents of that directory
179 File[] hostdirContents = hostdir.listFiles();
180 for(int j=0; j < hostdirContents.length; j++) {
181 File alertFile = hostdirContents[j];
182 // get the filename..
183 String filename = alertFile.getName();
184 // ..and see if it ends with OK or FINAL
185 if(filename.endsWith(Alert.alertLevels[0]) ||
186 filename.endsWith(Alert.alertLevels[Alert.alertLevels.length-1])) {
187 // it does end with either OK or FINAL
188 // ... so we can check it for deletion
189 long lastModified = alertFile.lastModified();
190 long age = System.currentTimeMillis() - lastModified;
191 if(age > ((long) deleteOlderThan*1000)) {
192 // if we're on a final heartbeat, we probably want to
193 // clean up any stale alerts left behind
194 // by setting this flag, we'll clean them up on leaving this loop
195 if(filename.endsWith(".HB."+Alert.alertLevels[Alert.alertLevels.length-1])) {
196 // we do this so that delete files is set to the
197 // latest date of a HB.FINAL. There should only be
198 // one of them though :)
199 if(lastModified > deleteFiles) {
200 deleteFiles = lastModified;
201 }
202 }
203 // it's also older than our age to delete older than
204 if(!alertFile.delete()) {
205 _logger.write(this.toString(), Logger.WARNING, "Failed to delete the following 'old' alert file: "+alertFile.getPath());
206 }
207 }
208 }
209 }
210 // cleanup stale alerts
211 if(deleteFiles >= 0) {
212 File[] remainingHostdirContents = hostdir.listFiles();
213 for(int j=0; j < remainingHostdirContents.length; j++) {
214 File alertFile = remainingHostdirContents[j];
215 if(alertFile.lastModified() < deleteFiles) {
216 // alert file is older than the most recent
217 // FINAL Heartbeat alert.
218 if(alertFile.delete()) {
219 _logger.write(this.toString(), Logger.DEBUG, "Deleted stale alert file: "+alertFile.getPath());
220 }
221 else {
222 _logger.write(this.toString(), Logger.WARNING, "Failed to delete the following 'stale' alert file: "+alertFile.getPath());
223 }
224 }
225 }
226 }
227 // ---- RECAP ----
228 // at this point, we have cleaned up any OK or FINAL alerts
229 // that have passed our age limit. We have then cleaned up
230 // any alerts older the most recent Heartbeat FINAL alert,
231 // as these are probably stale. Any files left are valid and
232 // active alerts. We are now in a position to remove the host
233 // directory if it's empty.
234 // ---------------
235 // do a quick check to see if the directory is now empty
236 File[] newHostdirContents = hostdir.listFiles();
237 if(newHostdirContents.length == 0) {
238 // it does seem to be, try and delete it
239 // this will fail anyway if files still remain
240 if(!hostdir.delete()) {
241 _logger.write(this.toString(), Logger.WARNING, "Failed to delete the following empty host directory: "+hostdir.getPath());
242 }
243 }
244 }
245 }
246 }
247 else {
248 _logger.write(toString(), Logger.WARNING, "IO error reading alerts directory, maybe it doesn't exist? : " +rootPath+sep+alertSubDir);
249 }
250 }
251 }
252
253 /**
254 * Handles an XMLPacket. This will write it out to disk
255 * in an appropriate manner.
256 *
257 * @param packet the XMLPacket to write
258 */
259 public void receiveXMLPacket(XMLPacket packet) {
260 String packetType = packet.getParam("packet.attributes.type");
261 if(packetType == null || !packetType.equals("data")) {
262 // bail out, without warning
263 // this is probably a heartbeat or similar
264 return;
265 }
266 // get config proxy
267 ConfigurationProxy cp = ConfigurationProxy.getInstance();
268 // get file locations
269 String rootPath, latestSubDir, latestFileName;
270 try {
271 rootPath = cp.getProperty("WebFeeder", "WebFeeder.rootPath");
272 latestSubDir = cp.getProperty("WebFeeder", "WebFeeder.latestSubDir");
273 latestFileName = cp.getProperty("WebFeeder", "WebFeeder.latestFileName");
274 } catch (PropertyNotFoundException e) {
275 _logger.write(this.toString(), Logger.ERROR, "Failed to get config for Latest Data: "+e);
276 // bail out
277 return;
278 }
279 // get raw data
280 String data = packet.printAll();
281 String hostname = packet.getParam("packet.attributes.machine_name");
282 // set paths
283 File outDir = new File(rootPath, latestSubDir+sep+hostname);
284 File outFile = new File(rootPath, latestSubDir+sep+hostname+sep+latestFileName);
285 // write the data out
286 writeData(outDir, outFile, data);
287 }
288
289 /**
290 * Handles an Alert. This will write it out to disk
291 * in an appropriate manner.
292 *
293 * @param alert the Alert object to write
294 */
295 public void receiveAlert(Alert alert) {
296 // get config proxy
297 ConfigurationProxy cp = ConfigurationProxy.getInstance();
298 // get file locations
299 String rootPath, alertSubDir, alertFileName;
300 try {
301 rootPath = cp.getProperty("WebFeeder", "WebFeeder.rootPath");
302 alertSubDir = cp.getProperty("WebFeeder", "WebFeeder.alertSubDir");
303 alertFileName = cp.getProperty("WebFeeder", "WebFeeder.alertFileName");
304 } catch (PropertyNotFoundException e) {
305 _logger.write(this.toString(), Logger.ERROR, "Failed to get config for Alert Data: "+e);
306 // bail out
307 return;
308 }
309 // get raw data
310 String data = alert.printAll();
311 String hostname = alert.getSource();
312 // set paths
313 File outDir = new File(rootPath, alertSubDir+sep+hostname);
314 String destFile = alertSubDir+sep+hostname+sep+alertFileName+"."+String.valueOf(alert.getInitialAlertTime());
315 File outFile;
316 // check if we're at a special "end case" (OK or FINAL)
317 if(alert.getLevel()==0 || alert.getLevel()==Alert.alertLevels.length-1) {
318 if(alert.getAttributeName().equals("Heartbeat") && alert.getLevel()==Alert.alertLevels.length-1) {
319 // new file is something like alert.nnnnnnnn.HB.FINAL
320 outFile = new File(rootPath, destFile+".HB."+Alert.alertLevels[alert.getLevel()]);
321 } else {
322 // new file is something like alert.nnnnnnnn.OK
323 outFile = new File(rootPath, destFile+"."+Alert.alertLevels[alert.getLevel()]);
324 }
325 File oldFile = new File(rootPath, destFile);
326 if(!oldFile.renameTo(outFile)) {
327 _logger.write(this.toString(), Logger.WARNING, "Failed to rename old file, "+oldFile.getPath()+" to new file, "+outFile.getPath());
328 }
329 } else {
330 outFile = new File(rootPath, destFile);
331 }
332 // write the data out
333 writeData(outDir, outFile, data);
334 }
335
336 /**
337 * Overrides the {@link java.lang.Object#toString() Object.toString()}
338 * method to provide clean logging (every class should have this).
339 *
340 * This uses the uk.org.iscream.cms.util.FormatName class
341 * to format the toString()
342 *
343 * @return the name of this class and its CVS revision
344 */
345 public String toString() {
346 return FormatName.getName(
347 _name,
348 getClass().getName(),
349 REVISION);
350 }
351
352 //---PRIVATE METHODS---
353
354 /**
355 * Attempts to write "data" to "outFile" in "outDir".
356 *
357 * Performs checks to create the directories, and the file.
358 * Does not "return" anything to indicate failure or success.
359 *
360 * @param outDir the directory to put the file in
361 * @param outFile the filename to put the data in
362 * @param data the String of data to write
363 */
364 private void writeData(File outDir, File outFile, String data) {
365 // try to create directory
366 if(!outDir.exists()) {
367 if(!outDir.mkdirs()) {
368 // didn't exist, and we couldn't make it
369 _logger.write(this.toString(), Logger.ERROR, "Failed to create directory: "+outDir.getPath());
370 // bail out
371 return;
372 }
373 }
374 // directory has been made, check file exists
375 if(!outFile.exists()) {
376 try {
377 outFile.createNewFile();
378 } catch (IOException e) {
379 _logger.write(this.toString(), Logger.ERROR, "Failed to create file: "+e);
380 // bail out
381 return;
382 }
383 }
384 // file should now exist
385 if(outFile.canWrite()) {
386 PrintWriter out;
387 try {
388 out = new PrintWriter(new FileWriter(outFile));
389 out.print(data + "\n");
390 out.flush();
391 out.close();
392 } catch (IOException e) {
393 _logger.write(this.toString(), Logger.ERROR, "Failed to write file: "+e);
394 }
395 }
396 else {
397 _logger.write(this.toString(), Logger.ERROR, "File not writeable: "+outFile.getPath());
398 }
399 }
400
401 /**
402 * Iterates through dir (a directory) and deletes
403 * all files and subdirectories.
404 *
405 * @param dir the directory to clear
406 * @return true if it succeeded
407 */
408 private boolean deleteContents(File dir) {
409 boolean success = true;
410 if(dir.isDirectory()) {
411 // is a directory
412 File[] contents = dir.listFiles();
413 for(int i=0; i < contents.length; i++) {
414 File sub = contents[i];
415 if(sub.isDirectory()) {
416 // lets get recursive
417 success = success & deleteContents(sub);
418 }
419 // it's a file or empty dir
420 success = success & sub.delete();
421 }
422 }
423 else {
424 // not a directory?
425 success=false;
426 }
427 return success;
428 }
429
430 //---ACCESSOR/MUTATOR METHODS---
431
432 //---ATTRIBUTES---
433
434 /**
435 * This is the friendly identifier of the
436 * component this class is running in.
437 * eg, a Filter may be called "filter1",
438 * If this class does not have an owning
439 * component, a name from the configuration
440 * can be placed here. This name could also
441 * be changed to null for utility classes.
442 */
443 private String _name = ClientMain.NAME;
444
445 /**
446 * This holds a reference to the
447 * system logger that is being used.
448 */
449 private Logger _logger = ReferenceManager.getInstance().getLogger();
450
451 //---STATIC ATTRIBUTES---
452
453 /**
454 * A reference to the single instance of this class
455 */
456 private static WebFeeder _instance;
457
458 }