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

# User Rev Content
1 tdb 1.1 //---PACKAGE DECLARATION---
2 tdb 1.7 package uk.org.iscream.client;
3 tdb 1.1
4     //---IMPORTS---
5 tdb 1.7 import uk.org.iscream.componentmanager.*;
6     import uk.org.iscream.core.*;
7     import uk.org.iscream.util.*;
8 tdb 1.2 import java.io.*;
9 tdb 1.1
10     /**
11     * Provides a feed to the webpage system.
12     *
13 tdb 1.11 * !! 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 tdb 1.9 *
17 tdb 1.2 * @author $Author: tdb1 $
18 tdb 1.18 * @version $Id: WebFeeder.java,v 1.17 2001/03/22 18:08:38 tdb1 Exp $
19 tdb 1.1 */
20 tdb 1.11 public class WebFeeder extends Thread {
21 tdb 1.1
22     //---FINAL ATTRIBUTES---
23    
24     /**
25     * The current CVS revision of this class
26     */
27 tdb 1.18 public static final String REVISION = "$Revision: 1.17 $";
28 tdb 1.11
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 tdb 1.1
39 tdb 1.14 /**
40     * The default path seperator, here for convienience
41     */
42     private final String sep = File.separator;
43    
44 tdb 1.1 //---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 tdb 1.17 public synchronized static WebFeeder getInstance() {
51 tdb 1.1 if (_instance == null){
52     _instance = new WebFeeder();
53     }
54     return _instance;
55     }
56    
57     //---CONSTRUCTORS---
58 tdb 1.11
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 tdb 1.1 private WebFeeder() {
65     // do something, or nothing.. but must be private
66 tdb 1.8 // don't need to cleanup latest data
67 tdb 1.9
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 tdb 1.12 File alertsDir = new File(rootPath, alertSubDir);
78 tdb 1.10 if(deleteContents(alertsDir)) {
79 tdb 1.14 _logger.write(this.toString(), Logger.DEBUG, "Deleted all files and directories from: "+rootPath+sep+alertSubDir);
80 tdb 1.10 } else {
81 tdb 1.14 _logger.write(this.toString(), Logger.WARNING, "Failed to delete all files and directories from: "+rootPath+sep+alertSubDir);
82 tdb 1.9 }
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 tdb 1.11
89     // set our name and startup
90     setName("client.WebFeeder");
91     start();
92 tdb 1.1 }
93    
94     //---PUBLIC METHODS---
95    
96 tdb 1.11 /**
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 tdb 1.12 // list the files and delete as appropriate
150     File alertsDir = new File(rootPath, alertSubDir);
151 tdb 1.15 // get all the hostnames directories
152 tdb 1.11 File[] contents = alertsDir.listFiles();
153     for(int i=0; i < contents.length; i++) {
154 tdb 1.15 // get a single directory from the array..
155 tdb 1.11 File hostdir = contents[i];
156 tdb 1.15 // ..and check it's a directory
157 tdb 1.12 if(hostdir.isDirectory()) {
158 tdb 1.18 // if this is set, we clean files older than it
159     long deleteFiles = -1;
160 tdb 1.15 // get all the contents of that directory
161 tdb 1.12 File[] hostdirContents = hostdir.listFiles();
162     for(int j=0; j < hostdirContents.length; j++) {
163     File alertFile = hostdirContents[j];
164 tdb 1.15 // get the filename..
165 tdb 1.12 String filename = alertFile.getName();
166 tdb 1.15 // ..and see if it ends with OK or FINAL
167 tdb 1.12 if(filename.endsWith(Alert.alertLevels[0]) ||
168     filename.endsWith(Alert.alertLevels[Alert.alertLevels.length-1])) {
169 tdb 1.15 // it does end with either OK or FINAL
170 tdb 1.12 // ... 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 tdb 1.18 // 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 tdb 1.12 // it's also older than our age to delete older than
186 tdb 1.18 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 tdb 1.16 if(alertFile.delete()) {
201 tdb 1.18 _logger.write(this.toString(), Logger.DEBUG, "Deleted stale alert file: "+alertFile.getPath());
202 tdb 1.16 }
203     else {
204 tdb 1.18 _logger.write(this.toString(), Logger.WARNING, "Failed to delete the following 'stale' alert file: "+alertFile.getPath());
205 tdb 1.12 }
206 tdb 1.11 }
207     }
208     }
209 tdb 1.18 // ---- 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 tdb 1.11 }
227     }
228     }
229     }
230 tdb 1.1
231 tdb 1.11 /**
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 tdb 1.1 public void receiveXMLPacket(XMLPacket packet) {
238 tdb 1.4 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 tdb 1.2 // 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 tdb 1.3 String hostname = packet.getParam("packet.attributes.machine_name");
260 tdb 1.2 // set paths
261 tdb 1.14 File outDir = new File(rootPath, latestSubDir+sep+hostname);
262     File outFile = new File(rootPath, latestSubDir+sep+hostname+sep+latestFileName);
263 tdb 1.11 // write the data out
264     writeData(outDir, outFile, data);
265 tdb 1.1 }
266    
267 tdb 1.11 /**
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 tdb 1.1 public void receiveAlert(Alert alert) {
274 tdb 1.5 // 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 tdb 1.14 File outDir = new File(rootPath, alertSubDir+sep+hostname);
292     String destFile = alertSubDir+sep+hostname+sep+alertFileName+"."+String.valueOf(alert.getInitialAlertTime());
293 tdb 1.9 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 tdb 1.18 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 tdb 1.12 File oldFile = new File(rootPath, destFile);
304 tdb 1.9 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 tdb 1.13 outFile = new File(rootPath, destFile);
309 tdb 1.9 }
310 tdb 1.11 // 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 tdb 1.5 // 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 tdb 1.8 out = new PrintWriter(new FileWriter(outFile));
367 tdb 1.5 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 tdb 1.1 }
377 tdb 1.10
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 tdb 1.1 //---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     }