View Javadoc

1   /**
2    *
3    * Copyright 2003-2004 The Apache Software Foundation
4    *
5    *  Licensed under the Apache License, Version 2.0 (the "License");
6    *  you may not use this file except in compliance with the License.
7    *  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  
18  package org.apache.geronimo.system.logging.log4j;
19  
20  import java.io.File;
21  import java.io.FileInputStream;
22  import java.io.FileOutputStream;
23  import java.io.IOException;
24  import java.io.InputStreamReader;
25  import java.io.OutputStream;
26  import java.io.RandomAccessFile;
27  import java.io.Reader;
28  import java.net.MalformedURLException;
29  import java.nio.CharBuffer;
30  import java.nio.MappedByteBuffer;
31  import java.nio.channels.FileChannel;
32  import java.nio.charset.Charset;
33  import java.util.ArrayList;
34  import java.util.Enumeration;
35  import java.util.Iterator;
36  import java.util.LinkedList;
37  import java.util.List;
38  import java.util.Set;
39  import java.util.Timer;
40  import java.util.TimerTask;
41  import java.util.regex.Matcher;
42  import java.util.regex.Pattern;
43  import java.util.regex.PatternSyntaxException;
44  
45  import org.apache.commons.logging.Log;
46  import org.apache.commons.logging.LogConfigurationException;
47  import org.apache.commons.logging.LogFactory;
48  import org.apache.geronimo.gbean.GBeanInfo;
49  import org.apache.geronimo.gbean.GBeanInfoBuilder;
50  import org.apache.geronimo.gbean.GBeanLifecycle;
51  import org.apache.geronimo.kernel.log.GeronimoLogFactory;
52  import org.apache.geronimo.kernel.log.GeronimoLogging;
53  import org.apache.geronimo.system.logging.SystemLog;
54  import org.apache.geronimo.system.serverinfo.DirectoryUtils;
55  import org.apache.geronimo.system.serverinfo.ServerConstants;
56  import org.apache.geronimo.system.serverinfo.ServerInfo;
57  import org.apache.log4j.FileAppender;
58  import org.apache.log4j.Level;
59  import org.apache.log4j.LogManager;
60  import org.apache.log4j.Logger;
61  
62  /**
63   * A Log4j logging service.
64   *
65   * @version $Rev: 471832 $ $Date: 2006-11-06 11:09:50 -0800 (Mon, 06 Nov 2006) $
66   */
67  public class Log4jService implements GBeanLifecycle, SystemLog {
68      // A substitution variable in the file path in the config file
69      private final static Pattern VARIABLE_PATTERN = Pattern.compile("\\$\\{.*?\\}");
70      // Next 6 are patterns that identify log messages in our default format
71      private final static Pattern DEFAULT_ANY_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (TRACE|DEBUG|INFO|WARN|ERROR|FATAL) .*");
72      private final static Pattern DEFAULT_FATAL_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d FATAL .*");
73      private final static Pattern DEFAULT_ERROR_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (ERROR|FATAL) .*");
74      private final static Pattern DEFAULT_WARN_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (WARN|ERROR|FATAL) .*");
75      private final static Pattern DEFAULT_INFO_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (INFO|WARN|ERROR|FATAL) .*");
76      private final static Pattern DEFAULT_DEBUG_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (DEBUG|INFO|WARN|ERROR|FATAL) .*");
77      // Next 6 are patterns that identify log messages if the user changed the format -- but we assume the log level is in there somewhere
78      private final static Pattern UNKNOWN_ANY_START = Pattern.compile("(TRACE|DEBUG|INFO|WARN|ERROR|FATAL)");
79      private final static Pattern UNKNOWN_FATAL_START = Pattern.compile("FATAL");
80      private final static Pattern UNKNOWN_ERROR_START = Pattern.compile("(ERROR|FATAL)");
81      private final static Pattern UNKNOWN_WARN_START = Pattern.compile("(WARN|ERROR|FATAL)");
82      private final static Pattern UNKNOWN_INFO_START = Pattern.compile("(INFO|WARN|ERROR|FATAL)");
83      private final static Pattern UNKNOWN_DEBUG_START = Pattern.compile("(DEBUG|INFO|WARN|ERROR|FATAL)");
84      // Pattern that matches a single line  (used to calculate line numbers and check for follow-on stack traces)
85      private final static Pattern FULL_LINE_PATTERN = Pattern.compile("^.*", Pattern.MULTILINE);
86  
87  
88      /**
89       * The URL to the configuration file.
90       */
91      private String configurationFile;
92  
93      /**
94       * The time (in seconds) between checking for new config.
95       */
96      private int refreshPeriod;
97  
98      /**
99       * The properties service
100      */
101     private final ServerInfo serverInfo;
102 
103     /**
104      * The URL watch timer (in daemon mode).
105      */
106     private Timer timer = new Timer(true);
107 
108     /**
109      * A monitor to check when the config URL changes.
110      */
111     private TimerTask monitor;
112 
113     /**
114      * Last time the file was changed.
115      */
116     private long lastChanged = -1;
117 
118     /**
119      * Is this service running?
120      */
121     private boolean running = false;
122 
123     /**
124      * Construct a <code>Log4jService</code>.
125      *
126      * @param configurationFile The log4j configuration file.
127      * @param refreshPeriod The refresh refreshPeriod (in seconds).
128      */
129     public Log4jService(final String configurationFile, final int refreshPeriod, ServerInfo serverInfo) {
130         this.refreshPeriod = refreshPeriod;
131         this.configurationFile = configurationFile;
132         this.serverInfo = serverInfo;
133     }
134 
135     /**
136      * Gets the level of the root logger.
137      */
138     public synchronized String getRootLoggerLevel() {
139         Level level = LogManager.getRootLogger().getLevel();
140 
141         if (level != null) {
142             return level.toString();
143         }
144 
145         return null;
146     }
147 
148     /**
149      * Sets the level of the root logger.
150      *
151      * @param level The level to change the logger to.
152      */
153     public synchronized void setRootLoggerLevel(final String level) {
154 
155         String currentLevel = this.getRootLoggerLevel();
156 
157         // ensure that the level has really been changed
158         if (!currentLevel.equals(level)) {
159             LogManager.getRootLogger().setLevel(XLevel.toLevel(level));
160         }
161     }
162 
163     /**
164      * Gets the level of the logger of the give name.
165      *
166      * @param logger The logger to inspect.
167      */
168     public String getLoggerEffectiveLevel(final String logger) {
169         if (logger == null) {
170             throw new IllegalArgumentException("logger is null");
171         }
172 
173         Level level = LogManager.getLogger(logger).getEffectiveLevel();
174 
175         if (level != null) {
176             return level.toString();
177         }
178 
179         return null;
180     }
181 
182     /**
183      * Gets the level of the logger of the give name.
184      *
185      * @param logger The logger to inspect.
186      */
187     public String getLoggerLevel(final String logger) {
188         if (logger == null) {
189             throw new IllegalArgumentException("logger is null");
190         }
191 
192         Level level = LogManager.getLogger(logger).getLevel();
193 
194         if (level != null) {
195             return level.toString();
196         }
197 
198         return null;
199     }
200 
201     /**
202      * Sets the level for a logger of the give name.
203      *
204      * @param logger The logger to change level
205      * @param level The level to change the logger to.
206      */
207     public void setLoggerLevel(final String logger, final String level) {
208         if (logger == null) {
209             throw new IllegalArgumentException("logger is null");
210         }
211         if (level == null) {
212             throw new IllegalArgumentException("level is null");
213         }
214 
215         Logger.getLogger(logger).setLevel(XLevel.toLevel(level));
216     }
217 
218     /**
219      * Get the refresh period.
220      *
221      * @return the refresh period (in seconds)
222      */
223     public synchronized int getRefreshPeriodSeconds() {
224         return refreshPeriod;
225     }
226 
227     /**
228      * Set the refresh period.
229      *
230      * @param period the refresh period (in seconds)
231      * @throws IllegalArgumentException if refresh period is <= 0
232      */
233     public synchronized void setRefreshPeriodSeconds(final int period) {
234         if (period < 1) {
235             throw new IllegalArgumentException("Refresh period must be > 0");
236         }
237 
238         if (this.refreshPeriod != period) {
239             this.refreshPeriod = period;
240             schedule();
241         }
242     }
243 
244     /**
245      * Get the logging configuration URL.
246      *
247      * @return the logging configuration URL
248      */
249     public synchronized String getConfigFileName() {
250         return configurationFile;
251     }
252 
253     /**
254      * Set the logging configuration URL.
255      *
256      * @param configurationFile the logging configuration file
257      */
258     public synchronized void setConfigFileName(final String configurationFile) {
259         if (configurationFile == null) {
260             throw new IllegalArgumentException("configurationFile is null");
261         }
262 
263         // ensure that the file name has really been updated
264         if (!this.configurationFile.equals(configurationFile)) {
265             this.configurationFile = configurationFile;
266             lastChanged = -1;
267         }
268     }
269 
270     /**
271      * Get the content of logging configuration file.
272      *
273      * @return the content of logging configuration file
274      */
275     public synchronized String getConfiguration() {
276         File file = resolveConfigurationFile();
277         if (file == null || !file.canRead()) {
278             return null;
279         }
280         Reader in = null;
281         try {
282             StringBuffer configuration = new StringBuffer();
283             in = new InputStreamReader(new FileInputStream(file));
284             char[] buffer = new char[4096];
285             for (int size = in.read(buffer); size >= 0; size = in.read(buffer)) {
286                 configuration.append(buffer, 0, size);
287             }
288             return configuration.toString();
289         } catch (IOException e) {
290             e.printStackTrace();
291         } finally {
292             if (in != null) {
293                 try {
294                     in.close();
295                 } catch (IOException e1) {
296                     e1.printStackTrace();
297                 }
298             }
299         }
300         return null;
301     }
302 
303     /**
304      * Overwrites the content of logging configuration file.
305      *
306      * @param configuration the new content of logging configuration file
307      */
308     public synchronized void setConfiguration(final String configuration) throws IOException {
309         if (configuration == null || configuration.length() == 0) {
310             throw new IllegalArgumentException("configuration is null or an empty string");
311         }
312 
313         File file = resolveConfigurationFile();
314         if (file == null) {
315             throw new IllegalStateException("Configuration file is null");
316         }
317 
318         // make parent directory if necessary
319         if (!file.getParentFile().exists()) {
320             if (!file.getParentFile().mkdirs()) {
321                 throw new IllegalStateException("Could not create parent directory of log configuration file: " + file.getParent());
322             }
323         }
324 
325         // verify that the file is writable or does not exist
326         if (file.exists() && !file.canWrite()) {
327             throw new IllegalStateException("Configuration file is not writable: " + file.getAbsolutePath());
328         }
329 
330         OutputStream out = null;
331         try {
332             out = new FileOutputStream(file);
333             out.write(configuration.getBytes());
334         } finally {
335             if (out != null) {
336                 try {
337                     out.close();
338                 } catch (IOException e) {
339                     e.printStackTrace();
340                 }
341             }
342         }
343     }
344 
345     public synchronized String[] getLogFileNames() {
346         List list = new ArrayList();
347         for (Enumeration e = Logger.getRootLogger().getAllAppenders(); e.hasMoreElements();) {
348             Object appender = e.nextElement();
349             if (appender instanceof FileAppender) {
350                 list.add(((FileAppender) appender).getFile());
351             }
352         }
353         return (String[]) list.toArray(new String[list.size()]);
354     }
355 
356     private static SearchResults searchFile(File file, String targetLevel, Pattern textSearch, Integer start, Integer stop, int max, boolean stacks) {
357         List list = new LinkedList();
358         boolean capped = false;
359         int lineCount = 0;
360         try {
361             RandomAccessFile raf = new RandomAccessFile(file, "r");
362             FileChannel fc = raf.getChannel();
363             MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
364             CharBuffer cb = Charset.forName("US-ASCII").decode(bb); //todo: does Log4J use a different charset on a foreign PC?
365             Matcher target = null;
366             Matcher any = null;
367             Matcher lines = FULL_LINE_PATTERN.matcher(cb);
368             Matcher text = textSearch == null ? null : textSearch.matcher("");
369             boolean hit = false;
370             max = Math.min(max, MAX_SEARCH_RESULTS);
371             while(lines.find()) {
372                 ++lineCount;
373                 if(target == null) {
374                     if(DEFAULT_ANY_START.matcher(cb.subSequence(lines.start(), lines.end())).find()) {
375                         target = getDefaultPatternForLevel(targetLevel).matcher("");
376                         any = DEFAULT_ANY_START.matcher("");
377                     } else {
378                         target = getUnknownPatternForLevel(targetLevel).matcher("");
379                         any = UNKNOWN_ANY_START.matcher("");
380                     }
381                 }
382                 if(start != null && start.intValue() > lineCount) {
383                     continue;
384                 }
385                 if(stop != null && stop.intValue() < lineCount) {
386                     continue;
387                 }
388                 CharSequence line = cb.subSequence(lines.start(), lines.end());
389                 target.reset(line);
390                 if(target.find()) {
391                     if(text != null) {
392                         text.reset(line);
393                         if(!text.find()) {
394                             hit = false;
395                             continue;
396                         }
397                     }
398                     list.add(new LogMessage(lineCount,line.toString()));
399                     if(list.size() > max) {
400                         list.remove(0);
401                         capped = true;
402                     }
403                     hit = true;
404                 } else if(stacks && hit) {
405                     any.reset(line);
406                     if(!any.find()) {
407                         list.add(new LogMessage(lineCount,line.toString()));
408                         if(list.size() > max) {
409                             list.remove(0);
410                             capped = true;
411                         }
412                     } else {
413                         hit = false;
414                     }
415                 }
416             }
417             fc.close();
418             raf.close();
419         } catch (Exception e) {} // TODO: improve exception handling
420         return new SearchResults(lineCount, (LogMessage[]) list.toArray(new LogMessage[list.size()]), capped);
421     }
422 
423     private static String substituteSystemProps(String source) {
424         StringBuffer buf = new StringBuffer();
425         int last = 0;
426         Matcher m = VARIABLE_PATTERN.matcher(source);
427         while(m.find()) {
428             buf.append(source.substring(last, m.start()));
429             String prop = source.substring(m.start()+2, m.end()-1);
430             buf.append(System.getProperty(prop));
431             last = m.end();
432         }
433         buf.append(source.substring(last));
434         return buf.toString();
435     }
436 
437     private static Pattern getDefaultPatternForLevel(String targetLevel) {
438         if(targetLevel.equals("FATAL")) {
439             return DEFAULT_FATAL_START;
440         } else if(targetLevel.equals("ERROR")) {
441             return DEFAULT_ERROR_START;
442         } else if(targetLevel.equals("WARN")) {
443             return DEFAULT_WARN_START;
444         } else if(targetLevel.equals("INFO")) {
445             return DEFAULT_INFO_START;
446         } else if(targetLevel.equals("DEBUG")) {
447             return DEFAULT_DEBUG_START;
448         } else {
449             return DEFAULT_ANY_START;
450         }
451     }
452 
453     private static Pattern getUnknownPatternForLevel(String targetLevel) {
454         if(targetLevel.equals("FATAL")) {
455             return UNKNOWN_FATAL_START;
456         } else if(targetLevel.equals("ERROR")) {
457             return UNKNOWN_ERROR_START;
458         } else if(targetLevel.equals("WARN")) {
459             return UNKNOWN_WARN_START;
460         } else if(targetLevel.equals("INFO")) {
461             return UNKNOWN_INFO_START;
462         } else if(targetLevel.equals("DEBUG")) {
463             return UNKNOWN_DEBUG_START;
464         } else {
465             return UNKNOWN_ANY_START;
466         }
467     }
468 
469     public SearchResults getMatchingItems(String logFile, Integer firstLine, Integer lastLine, String minLevel, String text, int maxResults, boolean includeStackTraces) {
470         // Ensure the file argument is really a log file!
471         if(logFile == null) {
472             throw new IllegalArgumentException("Must specify a log file");
473         }
474         String[] files = getLogFileNames();
475         boolean found = false;
476         for (int i = 0; i < files.length; i++) {
477             if(files[i].equals(logFile)) {
478                 found = true;
479                 break;
480             }
481         }
482         if(!found) {
483             throw new IllegalArgumentException("Not a log file!");
484         }
485         // Check for valid log level
486         if(minLevel == null) {
487             minLevel = "TRACE";
488         } else if(!minLevel.equals("FATAL") && !minLevel.equals("ERROR") && !minLevel.equals("WARN") &&
489                 !minLevel.equals("INFO") && !minLevel.equals("DEBUG") && !minLevel.equals("TRACE")) {
490             throw new IllegalArgumentException("Not a valid log level");
491         }
492         // Check that the text pattern is valid
493         Pattern textPattern = null;
494         try {
495             textPattern = text == null || text.equals("") ? null : Pattern.compile(text);
496         } catch (PatternSyntaxException e) {
497             throw new IllegalArgumentException("Bad regular expression '"+text+"'");
498         }
499         // Make sure we can find the log file
500         File log = new File(substituteSystemProps(logFile));
501         if(!log.exists()) {
502             throw new IllegalArgumentException("Log file "+log.getAbsolutePath()+" does not exist");
503         }
504         // Run the search
505         return searchFile(log, minLevel, textPattern, firstLine, lastLine, maxResults, includeStackTraces);
506     }
507 
508     /**
509      * Force the logging system to reconfigure.
510      */
511     public void reconfigure() {
512         File file = resolveConfigurationFile();
513         if (file == null || !file.exists()) {
514             return;
515         }
516 
517         // Record the default console log level
518         System.setProperty("org.apache.geronimo.log.ConsoleLogLevel", GeronimoLogging.getConsoleLogLevel().toString());
519 
520         try {
521             URLConfigurator.configure(file.toURL());
522         } catch (MalformedURLException e) {
523             e.printStackTrace();
524         }
525 
526         // refresh the level info for every log
527         GeronimoLogFactory logFactory = (GeronimoLogFactory) LogFactory.getFactory();
528         Set instances = logFactory.getInstances();
529         for (Iterator iterator = instances.iterator(); iterator.hasNext();) {
530             Object log = iterator.next();
531             if (log instanceof CachingLog4jLog) {
532                 ((CachingLog4jLog)log).updateLevelInfo();
533             }
534         }
535     }
536 
537     private synchronized void schedule() {
538         if (timer != null) {
539             // kill the old monitor
540             if (monitor != null) {
541                 monitor.cancel();
542             }
543 
544             // start the new one
545             monitor = new URLMonitorTask();
546             TimerTask task = monitor;
547             timer.schedule(monitor, 1000 * refreshPeriod, 1000 * refreshPeriod);
548             task.run();
549         }
550     }
551 
552     public void doStart() {
553         LogFactory logFactory = LogFactory.getFactory();
554         if (logFactory instanceof GeronimoLogFactory) {
555             synchronized (this) {
556                 timer = new Timer(true);
557 
558                 // Periodically check the configuration file
559                 schedule();
560 
561                 // Make sure the root Logger has loaded
562                 Logger logger = LogManager.getRootLogger();
563 
564                 reconfigure();
565 
566                 File file = resolveConfigurationFile();
567                 if (file != null) {
568                     lastChanged = file.lastModified();
569                 }
570                 logEnvInfo(logger);
571             }
572 
573             // Change all of the loggers over to use log4j
574             GeronimoLogFactory geronimoLogFactory = (GeronimoLogFactory) logFactory;
575             synchronized (geronimoLogFactory) {
576                 if (!(geronimoLogFactory.getLogFactory() instanceof CachingLog4jLogFactory)) {
577                     geronimoLogFactory.setLogFactory(new CachingLog4jLogFactory());
578                 }
579             }
580         }
581 
582         synchronized (this) {
583             running = true;
584         }
585     }
586 
587     public synchronized void doStop() {
588         running = false;
589         if (monitor != null) {
590             monitor.cancel();
591             monitor = null;
592         }
593         if (timer != null) {
594             timer.cancel();
595             timer = null;
596         }
597     }
598 
599     public void doFail() {
600         doStop();
601     }
602 
603     private synchronized File resolveConfigurationFile() {
604         try {
605             return serverInfo.resolveServer(configurationFile);
606         } catch (Exception e) {
607             return null;
608         }
609     }
610 
611     private void logEnvInfo(Logger log) {
612        try {
613           log.info("----------------------------------------------");
614           log.info("Started Logging Service");
615           log.debug("Log4jService created with configFileName=" + this.configurationFile +
616                     ", refreshPeriodSeconds=" + this.refreshPeriod);
617           log.info("Runtime Information:");
618           log.info("  Install Directory = " + DirectoryUtils.getGeronimoInstallDirectory().toString());
619           log.info("  JVM in use = " + System.getProperty("java.vendor") + " Java " + System.getProperty("java.version"));
620           log.info("Java Information:");
621           log.info("  System property [java.runtime.name]  = " + System.getProperty("java.runtime.name"));
622           log.info("  System property [java.runtime.version]  = " + System.getProperty("java.runtime.version"));
623           log.info("  System property [os.name]             = " + System.getProperty("os.name"));
624           log.info("  System property [os.version]          = " + System.getProperty("os.version"));
625           log.info("  System property [sun.os.patch.level]  = " + System.getProperty("sun.os.patch.level"));
626           log.info("  System property [os.arch]             = " + System.getProperty("os.arch"));
627           log.info("  System property [java.class.version]  = " + System.getProperty("java.class.version"));
628           log.info("  System property [locale]              = " + System.getProperty("user.language") + "_" + System.getProperty("user.country"));
629           log.info("  System property [unicode.encoding]    = " + System.getProperty("sun.io.unicode.encoding"));
630           log.info("  System property [file.encoding]       = " + System.getProperty("file.encoding"));
631           log.info("  System property [java.vm.name]        = " + System.getProperty("java.vm.name"));
632           log.info("  System property [java.vm.vendor]      = " + System.getProperty("java.vm.vendor"));
633           log.info("  System property [java.vm.version]     = " + System.getProperty("java.vm.version"));
634           log.info("  System property [java.vm.info]        = " + System.getProperty("java.vm.info"));
635           log.info("  System property [java.home]           = " + System.getProperty("java.home"));
636           log.info("  System property [java.classpath]      = " + System.getProperty("java.classpath"));
637           log.info("  System property [java.library.path]   = " + System.getProperty("java.library.path"));
638           log.info("  System property [java.endorsed.dirs]  = " + System.getProperty("java.endorsed.dirs"));
639           log.info("  System property [java.ext.dirs]       = " + System.getProperty("java.ext.dirs"));
640           log.info("  System property [sun.boot.class.path] = " + System.getProperty("sun.boot.class.path"));
641           log.info("----------------------------------------------");
642        } catch (Exception e) {
643           String msg = "Exception caught during logging of Runtime Information.  Exception=" + e.toString();
644           log.error(msg);
645           System.err.println(msg);
646        }
647     }
648 
649 
650     private class URLMonitorTask extends TimerTask {
651         public void run() {
652             try {
653                 long lastModified;
654                 synchronized (this) {
655                     if (running == false) {
656                         return;
657                     }
658 
659                     File file = resolveConfigurationFile();
660                     if (file == null) {
661                         return;
662                     }
663 
664                     lastModified = file.lastModified();
665                 }
666 
667                 if (lastChanged < lastModified) {
668                     lastChanged = lastModified;
669                     reconfigure();
670                 }
671             } catch (Exception e) {
672             }
673         }
674     }
675 
676     private static class CachingLog4jLogFactory extends LogFactory {
677         public Log getInstance(Class clazz) throws LogConfigurationException {
678             return getInstance(clazz.getName());
679         }
680 
681         public Log getInstance(String name) throws LogConfigurationException {
682             return new CachingLog4jLog(name);
683         }
684 
685         public Object getAttribute(String name) {
686             return null;
687         }
688 
689         public String[] getAttributeNames() {
690             return new String[0];
691         }
692 
693         public void release() {
694         }
695 
696         public void removeAttribute(String name) {
697         }
698 
699         public void setAttribute(String name, Object value) {
700         }
701     }
702 
703     public static final GBeanInfo GBEAN_INFO;
704 
705     static {
706         GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(Log4jService.class, "SystemLog");
707 
708         infoFactory.addAttribute("configFileName", String.class, true);
709         infoFactory.addAttribute("refreshPeriodSeconds", int.class, true);
710         infoFactory.addAttribute("configuration", String.class, false);
711         infoFactory.addAttribute("rootLoggerLevel", String.class, false);
712 
713         infoFactory.addReference("ServerInfo", ServerInfo.class, "GBean");
714 
715         infoFactory.addOperation("reconfigure");
716         infoFactory.addOperation("setLoggerLevel", new Class[]{String.class, String.class});
717         infoFactory.addOperation("getLoggerLevel", new Class[]{String.class});
718         infoFactory.addOperation("getLoggerEffectiveLevel", new Class[]{String.class});
719         infoFactory.addInterface(SystemLog.class);
720 
721         infoFactory.setConstructor(new String[]{"configFileName", "refreshPeriodSeconds", "ServerInfo"});
722 
723         GBEAN_INFO = infoFactory.getBeanInfo();
724     }
725 
726     public static GBeanInfo getGBeanInfo() {
727         return GBEAN_INFO;
728     }
729 }