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.20
Committed: Mon Dec 10 22:20:22 2001 UTC (22 years, 5 months ago) by tdb
Branch: MAIN
Changes since 1.19: +2 -6 lines
Log Message:
Some minor javadoc tweaks. The first sentence is now more obvious to the
javadoc parser.

File Contents

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