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.18
Committed: Fri Mar 23 00:44:11 2001 UTC (23 years, 1 month ago) by tdb
Branch: MAIN
CVS Tags: PROJECT_COMPLETION
Changes since 1.17: +55 -13 lines
Log Message:
Now does a cleanup of stale alerts left behind by a host after it shuts down.
This is done by removing any alerts older than a FINAL Heartbeat alert.

File Contents

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