001    /**
002     *
003     * Copyright 2003-2004 The Apache Software Foundation
004     *
005     *  Licensed under the Apache License, Version 2.0 (the "License");
006     *  you may not use this file except in compliance with the License.
007     *  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    
018    package org.apache.geronimo.system.logging.log4j;
019    
020    import java.io.File;
021    import java.io.FileInputStream;
022    import java.io.FileOutputStream;
023    import java.io.IOException;
024    import java.io.InputStreamReader;
025    import java.io.OutputStream;
026    import java.io.RandomAccessFile;
027    import java.io.Reader;
028    import java.net.MalformedURLException;
029    import java.nio.CharBuffer;
030    import java.nio.MappedByteBuffer;
031    import java.nio.channels.FileChannel;
032    import java.nio.charset.Charset;
033    import java.util.ArrayList;
034    import java.util.Enumeration;
035    import java.util.Iterator;
036    import java.util.LinkedList;
037    import java.util.List;
038    import java.util.Set;
039    import java.util.Timer;
040    import java.util.TimerTask;
041    import java.util.regex.Matcher;
042    import java.util.regex.Pattern;
043    import java.util.regex.PatternSyntaxException;
044    
045    import org.apache.commons.logging.Log;
046    import org.apache.commons.logging.LogConfigurationException;
047    import org.apache.commons.logging.LogFactory;
048    import org.apache.geronimo.gbean.GBeanInfo;
049    import org.apache.geronimo.gbean.GBeanInfoBuilder;
050    import org.apache.geronimo.gbean.GBeanLifecycle;
051    import org.apache.geronimo.kernel.log.GeronimoLogFactory;
052    import org.apache.geronimo.kernel.log.GeronimoLogging;
053    import org.apache.geronimo.system.logging.SystemLog;
054    import org.apache.geronimo.system.serverinfo.DirectoryUtils;
055    import org.apache.geronimo.system.serverinfo.ServerConstants;
056    import org.apache.geronimo.system.serverinfo.ServerInfo;
057    import org.apache.log4j.FileAppender;
058    import org.apache.log4j.Level;
059    import org.apache.log4j.LogManager;
060    import org.apache.log4j.Logger;
061    
062    /**
063     * A Log4j logging service.
064     *
065     * @version $Rev: 471832 $ $Date: 2006-11-06 11:09:50 -0800 (Mon, 06 Nov 2006) $
066     */
067    public class Log4jService implements GBeanLifecycle, SystemLog {
068        // A substitution variable in the file path in the config file
069        private final static Pattern VARIABLE_PATTERN = Pattern.compile("\\$\\{.*?\\}");
070        // Next 6 are patterns that identify log messages in our default format
071        private final static Pattern DEFAULT_ANY_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (TRACE|DEBUG|INFO|WARN|ERROR|FATAL) .*");
072        private final static Pattern DEFAULT_FATAL_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d FATAL .*");
073        private final static Pattern DEFAULT_ERROR_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (ERROR|FATAL) .*");
074        private final static Pattern DEFAULT_WARN_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (WARN|ERROR|FATAL) .*");
075        private final static Pattern DEFAULT_INFO_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (INFO|WARN|ERROR|FATAL) .*");
076        private final static Pattern DEFAULT_DEBUG_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (DEBUG|INFO|WARN|ERROR|FATAL) .*");
077        // Next 6 are patterns that identify log messages if the user changed the format -- but we assume the log level is in there somewhere
078        private final static Pattern UNKNOWN_ANY_START = Pattern.compile("(TRACE|DEBUG|INFO|WARN|ERROR|FATAL)");
079        private final static Pattern UNKNOWN_FATAL_START = Pattern.compile("FATAL");
080        private final static Pattern UNKNOWN_ERROR_START = Pattern.compile("(ERROR|FATAL)");
081        private final static Pattern UNKNOWN_WARN_START = Pattern.compile("(WARN|ERROR|FATAL)");
082        private final static Pattern UNKNOWN_INFO_START = Pattern.compile("(INFO|WARN|ERROR|FATAL)");
083        private final static Pattern UNKNOWN_DEBUG_START = Pattern.compile("(DEBUG|INFO|WARN|ERROR|FATAL)");
084        // Pattern that matches a single line  (used to calculate line numbers and check for follow-on stack traces)
085        private final static Pattern FULL_LINE_PATTERN = Pattern.compile("^.*", Pattern.MULTILINE);
086    
087    
088        /**
089         * The URL to the configuration file.
090         */
091        private String configurationFile;
092    
093        /**
094         * The time (in seconds) between checking for new config.
095         */
096        private int refreshPeriod;
097    
098        /**
099         * 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    }