KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > jmx > examples > scandir > ResultLogManager


1 /*
2  * ResultLogManager.java
3  *
4  * Created on July 17, 2006, 1:15 PM
5  *
6  * @(#)ResultLogManager.java 1.3 06/08/02
7  *
8  * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions are met:
12  *
13  * -Redistribution of source code must retain the above copyright notice, this
14  * list of conditions and the following disclaimer.
15  *
16  * -Redistribution in binary form must reproduce the above copyright notice,
17  * this list of conditions and the following disclaimer in the documentation
18  * and/or other materials provided with the distribution.
19  *
20  * Neither the name of Sun Microsystems, Inc. or the names of contributors may
21  * be used to endorse or promote products derived from this software without
22  * specific prior written permission.
23  *
24  * This software is provided "AS IS," without a warranty of any kind. ALL
25  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
26  * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
27  * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN")
28  * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
29  * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
30  * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
31  * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
32  * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
33  * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
34  * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
35  *
36  * You acknowledge that this software is not designed, licensed or intended
37  * for use in the design, construction, operation or maintenance of any
38  * nuclear facility.
39  */

40
41 package com.sun.jmx.examples.scandir;
42
43 import static com.sun.jmx.examples.scandir.ScanManager.getNextSeqNumber;
44 import com.sun.jmx.examples.scandir.config.ResultLogConfig;
45 import com.sun.jmx.examples.scandir.config.XmlConfigUtils;
46 import com.sun.jmx.examples.scandir.config.ResultRecord;
47 import java.io.File JavaDoc;
48 import java.io.FileOutputStream JavaDoc;
49 import java.io.IOException JavaDoc;
50 import java.io.OutputStream JavaDoc;
51 import java.util.Collections JavaDoc;
52 import java.util.LinkedList JavaDoc;
53 import java.util.List JavaDoc;
54 import java.util.logging.Logger JavaDoc;
55 import javax.management.MBeanNotificationInfo JavaDoc;
56 import javax.management.MBeanRegistration JavaDoc;
57 import javax.management.MBeanServer JavaDoc;
58 import javax.management.Notification JavaDoc;
59 import javax.management.NotificationBroadcasterSupport JavaDoc;
60 import javax.management.ObjectName JavaDoc;
61 import javax.xml.bind.JAXBException;
62
63 /**
64  * The <code>ResultLogManager</code> is in charge of managing result logs.
65  * {@link DirectoryScanner DirectoryScanners} can be configured to log a
66  * {@link ResultRecord} whenever they take action upon a file that
67  * matches their set of matching criteria.
68  * The <code>ResultLogManagerMXBean</code> is responsible for storing these
69  * results in its result logs.
70  * <p>The <code>ResultLogManagerMXBean</code> can be configured to log
71  * these records to a flat file, or into a log held in memory, or both.
72  * Both logs (file and memory) can be configured with a maximum capacity.
73  * <br>When the maximum capacity of the memory log is reached - its first
74  * entry (i.e. its eldest entry) is removed to make place for the latest.
75  * <br>When the maximum capacity of the file log is reached, the file is
76  * renamed by appending a tilde '~' to its name and a new result log is created.
77  *
78  *
79  * @author Sun Microsystems, 2006 - All rights reserved.
80  */

81 public class ResultLogManager extends NotificationBroadcasterSupport JavaDoc
82         implements ResultLogManagerMXBean, MBeanRegistration JavaDoc {
83     
84     /**
85      * The default singleton name of the {@link ResultLogManagerMXBean}.
86      **/

87     public static final ObjectName JavaDoc RESULT_LOG_MANAGER_NAME =
88             ScanManager.makeSingletonName(ResultLogManagerMXBean.class);
89     
90     /**
91      * A logger for this class.
92      **/

93     private static final Logger JavaDoc LOG =
94             Logger.getLogger(ResultLogManager.class.getName());
95     
96     // The memory log
97
//
98
private final List JavaDoc<ResultRecord> memoryLog;
99     
100     // Whether the memory log capacity was reached. In that case every
101
// new entry triggers the deletion of the eldest one.
102
//
103
private volatile boolean memCapacityReached = false;
104     
105     // The maximum number of record that the memory log can
106
// contain.
107
//
108
private volatile int memCapacity;
109     
110     // The maximum number of record that the ResultLogManager can
111
// log in the log file before creating a new file.
112
//
113
private volatile long fileCapacity;
114     
115     // The current log file.
116
//
117
private volatile File JavaDoc logFile;
118     
119     // The OutputStream of the current log file.
120
//
121
private volatile OutputStream JavaDoc logStream = null;
122     
123     // number of record that this object has logged in the log file
124
// since the log file was created. Creating a new file or clearing
125
// the log file reset this value to '0'
126
//
127
private volatile long logCount = 0;
128     
129     // The ResultLogManager config - modified whenever
130
// ScanManager.applyConfiguration is called.
131
//
132
private volatile ResultLogConfig config;
133     
134     /**
135      * Create a new ResultLogManagerMXBean. This constructor is package
136      * protected: only the {@link ScanManager} can create a
137      * <code>ResultLogManager</code>.
138      **/

139     ResultLogManager() {
140         // Instantiate the memory log - override the add() method so that
141
// it removes the head of the list when the maximum capacity is
142
// reached. Note that add() is the only method we will be calling,
143
// otherwise we would have to override all the other flavors
144
// of adding methods. Note also that this implies that the memoryLog
145
// will *always* remain encapsulated in this object and is *never*
146
// handed over (otherwise we wouldn't be able to ensure that
147
// add() is the only method ever called to add a record).
148
//
149
memoryLog =
150                 Collections.synchronizedList(new LinkedList JavaDoc<ResultRecord>() {
151             public synchronized boolean add(ResultRecord e) {
152                 final int max = getMemoryLogCapacity();
153                 while (max > 0 && size() >= max) {
154                     memCapacityReached = true;
155                     removeFirst();
156                 }
157                 return super.add(e);
158             }
159         });
160         
161         // default memory capacity
162
memCapacity = 2048;
163         
164         // default file capacity: 0 means infinite ;-)
165
fileCapacity = 0;
166         
167         // by default logging to file is disabled.
168
logFile = null;
169         
170         // Until the ScanManager apply a new configuration, we're going to
171
// work with a default ResultLogConfig object.
172
config = new ResultLogConfig();
173         config.setMemoryMaxRecords(memCapacity);
174         config.setLogFileName(getLogFileName(false));
175         config.setLogFileMaxRecords(fileCapacity);
176     }
177     
178     
179     /**
180      * Allows the MBean to perform any operations it needs before being
181      * registered in the MBean server.
182      * <p>If the name of the MBean is not
183      * specified, the MBean can provide a name for its registration. If
184      * any exception is raised, the MBean will not be registered in the
185      * MBean server.</p>
186      * <p>The {@code ResultLogManager} uses this method to supply its own
187      * default singleton ObjectName (if <var>name</var> parameter is null).
188      * @param server The MBean server in which the MBean will be registered.
189      * @param name The object name of the MBean. This name is null if the
190      * name parameter to one of the createMBean or registerMBean methods in
191      * the MBeanServer interface is null. In that case, this method must
192      * return a non-null ObjectName for the new MBean.
193      * @return The name under which the MBean is to be registered. This value
194      * must not be null. If the name parameter is not null, it will usually
195      * but not necessarily be the returned value.
196      * @throws Exception This exception will be caught by the MBean server and
197      * re-thrown as an MBeanRegistrationException.
198      */

199     public ObjectName JavaDoc preRegister(MBeanServer JavaDoc server, ObjectName JavaDoc name)
200     throws Exception JavaDoc {
201         if (name == null)
202             name = RESULT_LOG_MANAGER_NAME;
203         objectName = name;
204         mbeanServer = server;
205         return name;
206     }
207     
208     /**
209      * Allows the MBean to perform any operations needed after having
210      * been registered in the MBean server or after the registration has
211      * failed.
212      * <p>This implementation does nothing.</p>
213      * @param registrationDone Indicates whether or not the MBean has been
214      * successfully registered in the MBean server. The value false means
215      * that the registration has failed.
216      */

217     public void postRegister(Boolean JavaDoc registrationDone) {
218         // Don't need to do anything here.
219
}
220     
221     /**
222      * Allows the MBean to perform any operations it needs before being
223      * unregistered by the MBean server.
224      * <p>This implementation does nothing.</p>
225      * @throws Exception This exception will be caught by the MBean server and
226      * re-thrown as an MBeanRegistrationException.
227      */

228     public void preDeregister() throws Exception JavaDoc {
229         // Don't need to do anything here.
230
}
231     
232     /**
233      * Allows the MBean to perform any operations needed after having been
234      * unregistered in the MBean server.
235      * <p>Closes the log file stream, if it is still open.</p>
236      */

237     public void postDeregister() {
238         try {
239             if (logStream != null) {
240                 synchronized(this) {
241                     logStream.flush();
242                     logStream.close();
243                     logFile = null;
244                     logStream = null;
245                 }
246             }
247         } catch (Exception JavaDoc x) {
248             LOG.finest("Failed to close log properly: "+x);
249         }
250     }
251     
252     /**
253      * Create a new empty log file from the given basename, renaming
254      * previously existing file by appending '~' to its name.
255      **/

256     private File JavaDoc createNewLogFile(String JavaDoc basename) throws IOException JavaDoc {
257         return XmlConfigUtils.createNewXmlFile(basename);
258     }
259     
260     /**
261      * Check whether a new log file should be created.
262      * If a new file needs to be created, creates it, renaming
263      * previously existing file by appending '~' to its name.
264      * Also reset the log count and file capacity.
265      * Sends a notification indicating that the log file was changed.
266      * Returns the new log stream;
267      * Creation of a new file can be forced by passing force=true.
268      **/

269     private OutputStream JavaDoc checkLogFile(String JavaDoc basename, long maxRecords,
270                                       boolean force)
271     throws IOException JavaDoc {
272         final OutputStream JavaDoc newStream;
273         synchronized(this) {
274             if ((force==false) && (logCount < maxRecords))
275                 return logStream;
276             final OutputStream JavaDoc oldStream = logStream;
277             
278             // First close the stream. On some platforms you cannot rename
279
// a file that has open streams...
280
//
281
if (oldStream != null) {
282                 oldStream.flush();
283                 oldStream.close();
284             }
285             final File JavaDoc newFile = (basename==null)?null:createNewLogFile(basename);
286
287             newStream = (newFile==null)?null:new FileOutputStream JavaDoc(newFile,true);
288             logStream = newStream;
289             logFile = newFile;
290             fileCapacity = maxRecords;
291             logCount = 0;
292         }
293         sendNotification(new Notification JavaDoc(LOG_FILE_CHANGED,objectName,
294                 getNextSeqNumber(),
295                 basename));
296         return newStream;
297     }
298
299     // see ResultLogManagerMXBean
300
public void log(ResultRecord record)
301     throws IOException JavaDoc {
302         if (memCapacity > 0) logToMemory(record);
303         if (logFile != null) logToFile(record);
304     }
305     
306     // see ResultLogManagerMXBean
307
public ResultRecord[] getMemoryLog() {
308         return memoryLog.toArray(new ResultRecord[0]);
309     }
310     
311     // see ResultLogManagerMXBean
312
public int getMemoryLogCapacity() {
313         return memCapacity;
314     }
315     
316     // see ResultLogManagerMXBean
317
public void setMemoryLogCapacity(int maxRecords) {
318         synchronized(this) {
319             memCapacity = maxRecords;
320             if (memoryLog.size() < memCapacity)
321                 memCapacityReached = false;
322             config.setMemoryMaxRecords(maxRecords);
323         }
324     }
325     
326     // see ResultLogManagerMXBean
327
public void setLogFileCapacity(long maxRecords)
328     throws IOException JavaDoc {
329         synchronized (this) {
330             fileCapacity = maxRecords;
331             config.setLogFileMaxRecords(maxRecords);
332         }
333         checkLogFile(getLogFileName(),fileCapacity,false);
334     }
335     
336     // see ResultLogManagerMXBean
337
public long getLogFileCapacity() {
338         return fileCapacity;
339     }
340     
341     // see ResultLogManagerMXBean
342
public long getLoggedCount() {
343         return logCount;
344     }
345     
346     // see ResultLogManagerMXBean
347
public void newLogFile(String JavaDoc logname, long maxRecord)
348     throws IOException JavaDoc {
349         checkLogFile(logname,maxRecord,true);
350         config.setLogFileName(getLogFileName(false));
351         config.setLogFileMaxRecords(getLogFileCapacity());
352     }
353     
354     // see ResultLogManagerMXBean
355
public String JavaDoc getLogFileName() {
356         return getLogFileName(true);
357     }
358
359     // see ResultLogManagerMXBean
360
public void clearLogs() throws IOException JavaDoc {
361         clearMemoryLog();
362         clearLogFile();
363     }
364
365     // Clear the memory log, sends a notification indicating that
366
// the memory log was cleared.
367
//
368
private void clearMemoryLog()throws IOException JavaDoc {
369         synchronized(this) {
370             memoryLog.clear();
371             memCapacityReached = false;
372         }
373         sendNotification(new Notification JavaDoc(MEMORY_LOG_CLEARED,
374                 objectName,
375                 getNextSeqNumber(),"memory log cleared"));
376     }
377     
378     // Clears the log file.
379
//
380
private void clearLogFile() throws IOException JavaDoc {
381         // simply force the creation of a new log file.
382
checkLogFile(getLogFileName(),fileCapacity,true);
383     }
384     
385     // Log a record to the memory log. Send a notification if the
386
// maximum capacity of the memory log is reached.
387
//
388
private void logToMemory(ResultRecord record) {
389         
390         final boolean before = memCapacityReached;
391         final boolean after;
392         synchronized(this) {
393             memoryLog.add(record);
394             after = memCapacityReached;
395         }
396         if (before==false && after==true)
397             sendNotification(new Notification JavaDoc(MEMORY_LOG_MAX_CAPACITY,
398                     objectName,
399                     getNextSeqNumber(),"memory log capacity reached"));
400     }
401     
402     
403     // Log a record to the memory log. Send a notification if the
404
// maximum capacity of the memory log is reached.
405
//
406
private void logToFile(ResultRecord record) throws IOException JavaDoc {
407         final String JavaDoc basename;
408         final long maxRecords;
409         synchronized (this) {
410             if (logFile == null) return;
411             basename = getLogFileName(false);
412             maxRecords = fileCapacity;
413         }
414         
415         // Get the stream into which we should log.
416
final OutputStream JavaDoc stream =
417                 checkLogFile(basename,maxRecords,false);
418         
419         // logging to file now disabled - too bad.
420
if (stream == null) return;
421         
422         synchronized (this) {
423             try {
424                 XmlConfigUtils.write(record,stream,true);
425                 stream.flush();
426                 // don't increment logCount if we were not logging in logStream.
427
if (stream == logStream) logCount++;
428             } catch (JAXBException x) {
429                 final IllegalArgumentException JavaDoc iae =
430                         new IllegalArgumentException JavaDoc("bad record",x);
431                 LOG.finest("Failed to log record: "+x);
432                 throw iae;
433             }
434         }
435     }
436
437     /**
438      * The notification type which indicates that the log file was switched:
439      * <i>com.sun.jmx.examples.scandir.log.file.switched</i>.
440      * The message contains the name of the new file (or null if log to file
441      * is now disabled).
442      **/

443     public final static String JavaDoc LOG_FILE_CHANGED =
444             "com.sun.jmx.examples.scandir.log.file.switched";
445
446     /**
447      * The notification type which indicates that the memory log capacity has
448      * been reached:
449      * <i>com.sun.jmx.examples.scandir.log.memory.full</i>.
450      **/

451     public final static String JavaDoc MEMORY_LOG_MAX_CAPACITY =
452             "com.sun.jmx.examples.scandir.log.memory.full";
453
454     /**
455      * The notification type which indicates that the memory log was
456      * cleared:
457      * <i>com.sun.jmx.examples.scandir.log.memory.cleared</i>.
458      **/

459     public final static String JavaDoc MEMORY_LOG_CLEARED =
460             "com.sun.jmx.examples.scandir.log.memory.cleared";
461
462     /**
463      * This MBean emits three kind of notifications:
464      * <pre>
465      * <i>com.sun.jmx.examples.scandir.log.file.switched</i>
466      * <i>com.sun.jmx.examples.scandir.log.memory.full</i>
467      * <i>com.sun.jmx.examples.scandir.log.memory.cleared</i>
468      * </pre>
469      **/

470     public MBeanNotificationInfo JavaDoc[] getNotificationInfo() {
471         return new MBeanNotificationInfo JavaDoc[] {
472             new MBeanNotificationInfo JavaDoc(new String JavaDoc[] {
473                 LOG_FILE_CHANGED},
474                     Notification JavaDoc.class.getName(),
475                     "Emitted when the log file is switched")
476                     ,
477             new MBeanNotificationInfo JavaDoc(new String JavaDoc[] {
478                 MEMORY_LOG_MAX_CAPACITY},
479                     Notification JavaDoc.class.getName(),
480                     "Emitted when the memory log capacity is reached")
481                     ,
482             new MBeanNotificationInfo JavaDoc(new String JavaDoc[] {
483                 MEMORY_LOG_CLEARED},
484                     Notification JavaDoc.class.getName(),
485                     "Emitted when the memory log is cleared")
486         };
487     }
488
489     // Return the name of the log file, or null if logging to file is
490
// disabled.
491
private String JavaDoc getLogFileName(boolean absolute) {
492         synchronized (this) {
493             if (logFile == null) return null;
494             if (absolute) return logFile.getAbsolutePath();
495             return logFile.getPath();
496         }
497     }
498     
499     // This method is be called by the ScanManagerMXBean when a configuration
500
// is applied.
501
//
502
void setConfig(ResultLogConfig logConfigBean) throws IOException JavaDoc {
503         if (logConfigBean == null)
504             throw new IllegalArgumentException JavaDoc("logConfigBean is null");
505         synchronized (this) {
506             config = logConfigBean;
507             setMemoryLogCapacity(config.getMemoryMaxRecords());
508         }
509         final String JavaDoc filename = config.getLogFileName();
510         final String JavaDoc logname = getLogFileName(false);
511         if ((filename != null && !filename.equals(logname))
512         || (filename == null && logname != null)) {
513             newLogFile(config.getLogFileName(),
514                     config.getLogFileMaxRecords());
515         } else {
516             setLogFileCapacity(config.getLogFileMaxRecords());
517         }
518     }
519     
520     // This method is called by the ScanManagerMXBean when
521
// applyCurrentResultLogConfig() is called.
522
//
523
ResultLogConfig getConfig() {
524         return config;
525     }
526     
527     
528     // Set by preRegister().
529
private MBeanServer JavaDoc mbeanServer;
530     private ObjectName JavaDoc objectName;
531     
532     
533     
534 }
535
536
537
Popular Tags