001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  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.BufferedReader;
021    import java.io.File;
022    import java.io.FileInputStream;
023    import java.io.FileOutputStream;
024    import java.io.IOException;
025    import java.io.InputStreamReader;
026    import java.io.OutputStream;
027    import java.io.Reader;
028    import java.net.MalformedURLException;
029    import java.util.ArrayList;
030    import java.util.Enumeration;
031    import java.util.Iterator;
032    import java.util.LinkedList;
033    import java.util.List;
034    import java.util.Set;
035    import java.util.Timer;
036    import java.util.TimerTask;
037    import java.util.regex.Matcher;
038    import java.util.regex.Pattern;
039    import java.util.regex.PatternSyntaxException;
040    
041    import org.apache.commons.logging.Log;
042    import org.apache.commons.logging.LogConfigurationException;
043    import org.apache.commons.logging.LogFactory;
044    import org.apache.geronimo.gbean.GBeanInfo;
045    import org.apache.geronimo.gbean.GBeanInfoBuilder;
046    import org.apache.geronimo.gbean.GBeanLifecycle;
047    import org.apache.geronimo.kernel.log.GeronimoLogFactory;
048    import org.apache.geronimo.kernel.log.GeronimoLogging;
049    import org.apache.geronimo.system.logging.SystemLog;
050    import org.apache.geronimo.system.properties.JvmVendor;
051    import org.apache.geronimo.system.serverinfo.DirectoryUtils;
052    import org.apache.geronimo.system.serverinfo.ServerInfo;
053    import org.apache.log4j.FileAppender;
054    import org.apache.log4j.Level;
055    import org.apache.log4j.LogManager;
056    import org.apache.log4j.Logger;
057    
058    /**
059     * A Log4j logging service.
060     *
061     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
062     */
063    public class Log4jService implements GBeanLifecycle, SystemLog {
064        // A substitution variable in the file path in the config file
065        private final static Pattern VARIABLE_PATTERN = Pattern.compile("\\$\\{.*?\\}");
066        // Next 6 are patterns that identify log messages in our default format
067        private final static Pattern DEFAULT_ANY_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (TRACE|DEBUG|INFO|WARN|ERROR|FATAL) .*");
068        private final static Pattern DEFAULT_FATAL_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d FATAL .*");
069        private final static Pattern DEFAULT_ERROR_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (ERROR|FATAL) .*");
070        private final static Pattern DEFAULT_WARN_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (WARN|ERROR|FATAL) .*");
071        private final static Pattern DEFAULT_INFO_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (INFO|WARN|ERROR|FATAL) .*");
072        private final static Pattern DEFAULT_DEBUG_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (DEBUG|INFO|WARN|ERROR|FATAL) .*");
073        // Next 6 are patterns that identify log messages if the user changed the format -- but we assume the log level is in there somewhere
074        private final static Pattern UNKNOWN_ANY_START = Pattern.compile("(TRACE|DEBUG|INFO|WARN|ERROR|FATAL)");
075        private final static Pattern UNKNOWN_FATAL_START = Pattern.compile("FATAL");
076        private final static Pattern UNKNOWN_ERROR_START = Pattern.compile("(ERROR|FATAL)");
077        private final static Pattern UNKNOWN_WARN_START = Pattern.compile("(WARN|ERROR|FATAL)");
078        private final static Pattern UNKNOWN_INFO_START = Pattern.compile("(INFO|WARN|ERROR|FATAL)");
079        private final static Pattern UNKNOWN_DEBUG_START = Pattern.compile("(DEBUG|INFO|WARN|ERROR|FATAL)");
080    
081        private final static Log log = LogFactory.getLog(Log4jService.class);
082    
083        private static final String LOG4JSERVICE_CONFIG_PROPERTY = "org.apache.geronimo.log4jservice.configuration";
084    
085        /**
086         * The URL to the configuration file.
087         */
088        private String configurationFile;
089    
090        /**
091         * The time (in seconds) between checking for new config.
092         */
093        private int refreshPeriod;
094    
095        /**
096         * The properties service
097         */
098        private final ServerInfo serverInfo;
099    
100        /**
101         * The URL watch timer (in daemon mode).
102         */
103        private Timer timer = new Timer(true);
104    
105        /**
106         * A monitor to check when the config URL changes.
107         */
108        private TimerTask monitor;
109    
110        /**
111         * Last time the file was changed.
112         */
113        private long lastChanged = -1;
114    
115        /**
116         * Is this service running?
117         */
118        private boolean running = false;
119    
120        /**
121         * Construct a <code>Log4jService</code>.
122         *
123         * @param configurationFile The log4j configuration file.
124         * @param refreshPeriod The refresh refreshPeriod (in seconds).
125         */
126        public Log4jService(final String configurationFile, final int refreshPeriod, ServerInfo serverInfo) {
127            this.refreshPeriod = refreshPeriod;
128            this.configurationFile = configurationFile;
129            this.serverInfo = serverInfo;
130            Logger.getLogger(this.getClass().getName()).setLevel(Level.INFO);
131        }
132    
133        /**
134         * Gets the level of the root logger.
135         */
136        public synchronized String getRootLoggerLevel() {
137            Level level = LogManager.getRootLogger().getLevel();
138    
139            if (level != null) {
140                return level.toString();
141            }
142    
143            return null;
144        }
145    
146        /**
147         * Sets the level of the root logger.
148         *
149         * @param level The level to change the logger to.
150         */
151        public synchronized void setRootLoggerLevel(final String level) {
152    
153            String currentLevel = this.getRootLoggerLevel();
154    
155            // ensure that the level has really been changed
156            if (!currentLevel.equals(level)) {
157                LogManager.getRootLogger().setLevel(XLevel.toLevel(level));
158            }
159        }
160    
161        /**
162         * Gets the level of the logger of the give name.
163         *
164         * @param logger The logger to inspect.
165         */
166        public String getLoggerEffectiveLevel(final String logger) {
167            if (logger == null) {
168                throw new IllegalArgumentException("logger is null");
169            }
170    
171            Level level = LogManager.getLogger(logger).getEffectiveLevel();
172    
173            if (level != null) {
174                return level.toString();
175            }
176    
177            return null;
178        }
179    
180        /**
181         * Gets the level of the logger of the give name.
182         *
183         * @param logger The logger to inspect.
184         */
185        public String getLoggerLevel(final String logger) {
186            if (logger == null) {
187                throw new IllegalArgumentException("logger is null");
188            }
189    
190            Level level = LogManager.getLogger(logger).getLevel();
191    
192            if (level != null) {
193                return level.toString();
194            }
195    
196            return null;
197        }
198    
199        /**
200         * Sets the level for a logger of the give name.
201         *
202         * @param logger The logger to change level
203         * @param level The level to change the logger to.
204         */
205        public void setLoggerLevel(final String logger, final String level) {
206            if (logger == null) {
207                throw new IllegalArgumentException("logger is null");
208            }
209            if (level == null) {
210                throw new IllegalArgumentException("level is null");
211            }
212    
213            log.info("Setting logger level: logger=" + logger + ", level=" + level);
214            Logger.getLogger(logger).setLevel(XLevel.toLevel(level));
215        }
216    
217        /**
218         * Get the refresh period.
219         *
220         * @return the refresh period (in seconds)
221         */
222        public synchronized int getRefreshPeriodSeconds() {
223            return refreshPeriod;
224        }
225    
226        /**
227         * Set the refresh period.
228         *
229         * @param period the refresh period (in seconds)
230         * @throws IllegalArgumentException if refresh period is < 5
231         */
232        public synchronized void setRefreshPeriodSeconds(final int period) {
233            if (period < 5) {
234                throw new IllegalArgumentException("Refresh period must be at least 5 seconds");
235            }
236    
237            if (this.refreshPeriod != period) {
238                this.refreshPeriod = period;
239                schedule();
240            }
241        }
242    
243        /**
244         * Get the logging configuration URL.
245         *
246         * @return the logging configuration URL
247         */
248        public synchronized String getConfigFileName() {
249            return configurationFile;
250        }
251    
252        /**
253         * Set the logging configuration URL.
254         *
255         * @param configurationFile the logging configuration file
256         */
257        public synchronized void setConfigFileName(final String configurationFile) {
258            if (log.isDebugEnabled()) {
259                log.debug("setConfigFileName() called with configurationFile=" + configurationFile);
260            }
261    
262            if (configurationFile == null) {
263                throw new IllegalArgumentException("configurationFile is null");
264            }
265    
266            // ensure that the file name has really been updated
267            if (!this.configurationFile.equals(configurationFile)) {
268                this.configurationFile = configurationFile;
269                lastChanged = -1;
270                reconfigure();
271            }
272        }
273    
274        /**
275         * Get the content of logging configuration file.
276         *
277         * @return the content of logging configuration file
278         */
279        public synchronized String getConfiguration() {
280            File file = resolveConfigurationFile();
281            if (file == null || !file.canRead()) {
282                return null;
283            }
284            Reader in = null;
285            try {
286                StringBuffer configuration = new StringBuffer();
287                in = new InputStreamReader(new FileInputStream(file));
288                char[] buffer = new char[4096];
289                for (int size = in.read(buffer); size >= 0; size = in.read(buffer)) {
290                    configuration.append(buffer, 0, size);
291                }
292                return configuration.toString();
293            } catch (IOException e) {
294                e.printStackTrace();
295            } finally {
296                if (in != null) {
297                    try {
298                        in.close();
299                    } catch (IOException e1) {
300                        e1.printStackTrace();
301                    }
302                }
303            }
304            return null;
305        }
306    
307        /**
308         * Overwrites the content of logging configuration file.
309         *
310         * @param configuration the new content of logging configuration file
311         */
312        public synchronized void setConfiguration(final String configuration) throws IOException {
313            if (configuration == null || configuration.length() == 0) {
314                throw new IllegalArgumentException("configuration is null or an empty string");
315            }
316    
317            File file = resolveConfigurationFile();
318            if (file == null) {
319                throw new IllegalStateException("Configuration file is null");
320            }
321    
322            // make parent directory if necessary
323            if (!file.getParentFile().exists()) {
324                if (!file.getParentFile().mkdirs()) {
325                    throw new IllegalStateException("Could not create parent directory of log configuration file: " + file.getParent());
326                }
327            }
328    
329            // verify that the file is writable or does not exist
330            if (file.exists() && !file.canWrite()) {
331                throw new IllegalStateException("Configuration file is not writable: " + file.getAbsolutePath());
332            }
333    
334            OutputStream out = null;
335            try {
336                out = new FileOutputStream(file);
337                out.write(configuration.getBytes());
338                log.info("Updated configuration file=" + file.toString());
339            } finally {
340                if (out != null) {
341                    try {
342                        out.close();
343                    } catch (IOException e) {
344                        e.printStackTrace();
345                    }
346                }
347            }
348        }
349    
350        public synchronized String[] getLogFileNames() {
351            List list = new ArrayList();
352            for (Enumeration e = Logger.getRootLogger().getAllAppenders(); e.hasMoreElements();) {
353                Object appender = e.nextElement();
354                if (appender instanceof FileAppender) {
355                    list.add(((FileAppender) appender).getFile());
356                }
357            }
358            return (String[]) list.toArray(new String[list.size()]);
359        }
360    
361        private static SearchResults searchFile(File file, String targetLevel, Pattern textSearch, Integer start, Integer stop, int max, boolean stacks) {
362            List list = new LinkedList();
363            boolean capped = false;
364            int lineCount = 0;
365            FileInputStream logInputStream = null;
366            try {
367                logInputStream = new FileInputStream(file);
368                BufferedReader reader = new BufferedReader(new InputStreamReader(logInputStream, System.getProperty("file.encoding")));
369                Matcher target = null;
370                Matcher any = null;
371                Matcher text = textSearch == null ? null : textSearch.matcher("");
372                boolean hit = false;
373                max = Math.min(max, MAX_SEARCH_RESULTS);
374                String line;
375                while ((line = reader.readLine()) != null) {
376                    ++lineCount;
377                    if(target == null) {
378                        if(DEFAULT_ANY_START.matcher(line).find()) {
379                            target = getDefaultPatternForLevel(targetLevel).matcher("");
380                            any = DEFAULT_ANY_START.matcher("");
381                        } else {
382                            target = getUnknownPatternForLevel(targetLevel).matcher("");
383                            any = UNKNOWN_ANY_START.matcher("");
384                        }
385                    }
386                    if(start != null && start.intValue() > lineCount) {
387                        continue;
388                    }
389                    if(stop != null && stop.intValue() < lineCount) {
390                        continue;
391                    }
392                    target.reset(line);
393                    if(target.find()) {
394                        if(text != null) {
395                            text.reset(line);
396                            if(!text.find()) {
397                                hit = false;
398                                continue;
399                            }
400                        }
401                        list.add(new LogMessage(lineCount,line.toString()));
402                        if(list.size() > max) {
403                            list.remove(0);
404                            capped = true;
405                        }
406                        hit = true;
407                    } else if(stacks && hit) {
408                        any.reset(line);
409                        if(!any.find()) {
410                            list.add(new LogMessage(lineCount,line.toString()));
411                            if(list.size() > max) {
412                                list.remove(0);
413                                capped = true;
414                            }
415                        } else {
416                            hit = false;
417                        }
418                    }
419                }
420            } catch (Exception e) {
421                // TODO: improve exception handling
422            } finally {
423                if (logInputStream != null) {
424                    try {
425                        logInputStream.close();
426                    } catch (IOException e) {
427                        // ignore
428                    }
429                }
430            }
431            return new SearchResults(lineCount, (LogMessage[]) list.toArray(new LogMessage[list.size()]), capped);
432        }
433    
434        private static String substituteSystemProps(String source) {
435            StringBuffer buf = new StringBuffer();
436            int last = 0;
437            Matcher m = VARIABLE_PATTERN.matcher(source);
438            while(m.find()) {
439                buf.append(source.substring(last, m.start()));
440                String prop = source.substring(m.start()+2, m.end()-1);
441                buf.append(System.getProperty(prop));
442                last = m.end();
443            }
444            buf.append(source.substring(last));
445            return buf.toString();
446        }
447    
448        private static Pattern getDefaultPatternForLevel(String targetLevel) {
449            if(targetLevel.equals("FATAL")) {
450                return DEFAULT_FATAL_START;
451            } else if(targetLevel.equals("ERROR")) {
452                return DEFAULT_ERROR_START;
453            } else if(targetLevel.equals("WARN")) {
454                return DEFAULT_WARN_START;
455            } else if(targetLevel.equals("INFO")) {
456                return DEFAULT_INFO_START;
457            } else if(targetLevel.equals("DEBUG")) {
458                return DEFAULT_DEBUG_START;
459            } else {
460                return DEFAULT_ANY_START;
461            }
462        }
463    
464        private static Pattern getUnknownPatternForLevel(String targetLevel) {
465            if(targetLevel.equals("FATAL")) {
466                return UNKNOWN_FATAL_START;
467            } else if(targetLevel.equals("ERROR")) {
468                return UNKNOWN_ERROR_START;
469            } else if(targetLevel.equals("WARN")) {
470                return UNKNOWN_WARN_START;
471            } else if(targetLevel.equals("INFO")) {
472                return UNKNOWN_INFO_START;
473            } else if(targetLevel.equals("DEBUG")) {
474                return UNKNOWN_DEBUG_START;
475            } else {
476                return UNKNOWN_ANY_START;
477            }
478        }
479    
480        public SearchResults getMatchingItems(String logFile, Integer firstLine, Integer lastLine, String minLevel, String text, int maxResults, boolean includeStackTraces) {
481            // Ensure the file argument is really a log file!
482            if(logFile == null) {
483                throw new IllegalArgumentException("Must specify a log file");
484            }
485            String[] files = getLogFileNames();
486            boolean found = false;
487            for (int i = 0; i < files.length; i++) {
488                if(files[i].equals(logFile)) {
489                    found = true;
490                    break;
491                }
492            }
493            if(!found) {
494                throw new IllegalArgumentException("Not a log file!");
495            }
496            // Check for valid log level
497            if(minLevel == null) {
498                minLevel = "TRACE";
499            } else if(!minLevel.equals("FATAL") && !minLevel.equals("ERROR") && !minLevel.equals("WARN") &&
500                    !minLevel.equals("INFO") && !minLevel.equals("DEBUG") && !minLevel.equals("TRACE")) {
501                throw new IllegalArgumentException("Not a valid log level");
502            }
503            // Check that the text pattern is valid
504            Pattern textPattern = null;
505            try {
506                textPattern = text == null || text.equals("") ? null : Pattern.compile(text);
507            } catch (PatternSyntaxException e) {
508                throw new IllegalArgumentException("Bad regular expression '"+text+"'", e);
509            }
510            // Make sure we can find the log file
511            File log = new File(substituteSystemProps(logFile));
512            if(!log.exists()) {
513                throw new IllegalArgumentException("Log file "+log.getAbsolutePath()+" does not exist");
514            }
515            // Run the search
516            return searchFile(log, minLevel, textPattern, firstLine, lastLine, maxResults, includeStackTraces);
517        }
518    
519        /**
520         * Force the logging system to reconfigure.
521         */
522        public void reconfigure() {
523            File file = resolveConfigurationFile();
524            if (file == null || !file.exists()) {
525                return;
526            } else {
527                if (log.isDebugEnabled()) {
528                    log.debug("reconfigure() using configurationFile=" + configurationFile);
529                }
530                lastChanged = file.lastModified();
531            }
532    
533            // Record the default console log level
534            System.setProperty("org.apache.geronimo.log.ConsoleLogLevel", GeronimoLogging.getConsoleLogLevel().toString());
535    
536            try {
537                URLConfigurator.configure(file.toURL());
538            } catch (MalformedURLException e) {
539                e.printStackTrace();
540            }
541    
542            // refresh the level info for every log
543            GeronimoLogFactory logFactory = (GeronimoLogFactory) LogFactory.getFactory();
544            Set instances = logFactory.getInstances();
545            for (Iterator iterator = instances.iterator(); iterator.hasNext();) {
546                Object log = iterator.next();
547                if (log instanceof CachingLog4jLog) {
548                    ((CachingLog4jLog)log).updateLevelInfo();
549                }
550            }
551        }
552    
553        private synchronized void schedule() {
554            if (timer != null) {
555                // kill the old monitor
556                if (monitor != null) {
557                    monitor.cancel();
558                }
559    
560                // start the new one
561                monitor = new URLMonitorTask();
562                TimerTask task = monitor;
563                timer.schedule(monitor, 1000 * refreshPeriod, 1000 * refreshPeriod);
564                task.run();
565            }
566        }
567    
568        public void doStart() {
569            LogFactory logFactory = LogFactory.getFactory();
570            if (logFactory instanceof GeronimoLogFactory) {
571                // Make sure the root Logger has loaded
572                Logger logger = LogManager.getRootLogger();
573    
574                // Change all of the loggers over to use log4j
575                GeronimoLogFactory geronimoLogFactory = (GeronimoLogFactory) logFactory;
576                synchronized (geronimoLogFactory) {
577                    if (!(geronimoLogFactory.getLogFactory() instanceof CachingLog4jLogFactory)) {
578                        geronimoLogFactory.setLogFactory(new CachingLog4jLogFactory());
579                    }
580                }
581    
582                // Allow users to override the configurationFile which is hardcoded
583                // in config.ser and cannot be updated by config.xml, as the
584                // AttrbiuteManager comes up after this GBean
585                String cfgFile = System.getProperty(LOG4JSERVICE_CONFIG_PROPERTY);
586                if ((cfgFile != null) && (!cfgFile.equals(""))) {
587                    this.configurationFile = cfgFile;
588                }
589    
590                synchronized (this) {
591                    reconfigure();
592    
593                    timer = new Timer(true);
594    
595                    // Periodically check the configuration file
596                    schedule();
597                }
598    
599                logEnvInfo();
600            }
601    
602            synchronized (this) {
603                running = true;
604            }
605        }
606    
607        public synchronized void doStop() {
608            running = false;
609            if (monitor != null) {
610                monitor.cancel();
611                monitor = null;
612            }
613            if (timer != null) {
614                timer.cancel();
615                timer = null;
616            }
617        }
618    
619        public void doFail() {
620            doStop();
621        }
622    
623        private synchronized File resolveConfigurationFile() {
624            try {
625                return serverInfo.resolveServer(configurationFile);
626            } catch (Exception e) {
627                return null;
628            }
629        }
630    
631        private void logEnvInfo() {
632           try {
633              Log log = LogFactory.getLog(Log4jService.class);
634              log.info("----------------------------------------------");
635              log.info("Started Logging Service");
636              if (log.isDebugEnabled()) {
637                  log.debug("Log4jService created with configFileName=" + this.configurationFile + ", refreshPeriodSeconds=" + this.refreshPeriod);
638              }
639              log.info("Runtime Information:");
640              log.info("  Install Directory = " + DirectoryUtils.getGeronimoInstallDirectory().toString());
641              log.info("  JVM in use = " + JvmVendor.getJvmInfo());
642              log.info("Java Information:");
643              log.info("  System property [java.runtime.name]  = " + System.getProperty("java.runtime.name"));
644              log.info("  System property [java.runtime.version]  = " + System.getProperty("java.runtime.version"));
645              log.info("  System property [os.name]             = " + System.getProperty("os.name"));
646              log.info("  System property [os.version]          = " + System.getProperty("os.version"));
647              log.info("  System property [sun.os.patch.level]  = " + System.getProperty("sun.os.patch.level"));
648              log.info("  System property [os.arch]             = " + System.getProperty("os.arch"));
649              log.info("  System property [java.class.version]  = " + System.getProperty("java.class.version"));
650              log.info("  System property [locale]              = " + System.getProperty("user.language") + "_" + System.getProperty("user.country"));
651              log.info("  System property [unicode.encoding]    = " + System.getProperty("sun.io.unicode.encoding"));
652              log.info("  System property [file.encoding]       = " + System.getProperty("file.encoding"));
653              log.info("  System property [java.vm.name]        = " + System.getProperty("java.vm.name"));
654              log.info("  System property [java.vm.vendor]      = " + System.getProperty("java.vm.vendor"));
655              log.info("  System property [java.vm.version]     = " + System.getProperty("java.vm.version"));
656              log.info("  System property [java.vm.info]        = " + System.getProperty("java.vm.info"));
657              log.info("  System property [java.home]           = " + System.getProperty("java.home"));
658              log.info("  System property [java.classpath]      = " + System.getProperty("java.classpath"));
659              log.info("  System property [java.library.path]   = " + System.getProperty("java.library.path"));
660              log.info("  System property [java.endorsed.dirs]  = " + System.getProperty("java.endorsed.dirs"));
661              log.info("  System property [java.ext.dirs]       = " + System.getProperty("java.ext.dirs"));
662              log.info("  System property [sun.boot.class.path] = " + System.getProperty("sun.boot.class.path"));
663              log.info("----------------------------------------------");
664           } catch (Exception e) {
665              System.err.println("Exception caught during logging of Runtime Information.  Exception=" + e.toString());
666           }
667        }
668    
669    
670        private class URLMonitorTask extends TimerTask {
671            public void run() {
672                try {
673                    long lastModified;
674                    synchronized (this) {
675                        if (running == false) {
676                            return;
677                        }
678    
679                        File file = resolveConfigurationFile();
680                        if (file == null) {
681                            return;
682                        }
683    
684                        lastModified = file.lastModified();
685                    }
686    
687                    if (lastChanged < lastModified) {
688                        lastChanged = lastModified;
689                        reconfigure();
690                    }
691                } catch (Exception e) {
692                }
693            }
694        }
695    
696        private static class CachingLog4jLogFactory extends LogFactory {
697            public Log getInstance(Class clazz) throws LogConfigurationException {
698                return getInstance(clazz.getName());
699            }
700    
701            public Log getInstance(String name) throws LogConfigurationException {
702                return new CachingLog4jLog(name);
703            }
704    
705            public Object getAttribute(String name) {
706                return null;
707            }
708    
709            public String[] getAttributeNames() {
710                return new String[0];
711            }
712    
713            public void release() {
714            }
715    
716            public void removeAttribute(String name) {
717            }
718    
719            public void setAttribute(String name, Object value) {
720            }
721        }
722    
723        public static final GBeanInfo GBEAN_INFO;
724    
725        static {
726            GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(Log4jService.class, "SystemLog");
727    
728            infoFactory.addAttribute("configFileName", String.class, true);
729            infoFactory.addAttribute("refreshPeriodSeconds", int.class, true);
730            infoFactory.addAttribute("configuration", String.class, false);
731            infoFactory.addAttribute("rootLoggerLevel", String.class, false);
732    
733            infoFactory.addReference("ServerInfo", ServerInfo.class, "GBean");
734    
735            infoFactory.addOperation("reconfigure");
736            infoFactory.addOperation("setLoggerLevel", new Class[]{String.class, String.class});
737            infoFactory.addOperation("getLoggerLevel", new Class[]{String.class});
738            infoFactory.addOperation("getLoggerEffectiveLevel", new Class[]{String.class});
739            infoFactory.addInterface(SystemLog.class);
740    
741            infoFactory.setConstructor(new String[]{"configFileName", "refreshPeriodSeconds", "ServerInfo"});
742    
743            GBEAN_INFO = infoFactory.getBeanInfo();
744        }
745    
746        public static GBeanInfo getGBeanInfo() {
747            return GBEAN_INFO;
748        }
749    }