001    /**
002     * Source code taken from {@link http://wiki.eclipse.org/EclipseLink/Foundation/Logging}.
003     * Supports use of log4j with eclipselink.
004     */
005    package org.eclipse.persistence.logging;
006    
007    import java.io.OutputStream;
008    import java.security.AccessController;
009    import java.security.PrivilegedAction;
010    import java.util.ArrayList;
011    import java.util.HashMap;
012    import java.util.List;
013    import java.util.Map;
014    import java.util.logging.Level;
015    
016    import org.apache.commons.logging.Log;
017    import org.apache.commons.logging.LogFactory;
018    import org.apache.commons.logging.impl.Log4JLogger;
019    import org.apache.log4j.Logger;
020    import org.eclipse.persistence.logging.AbstractSessionLog;
021    import org.eclipse.persistence.logging.EclipseLinkLogRecord;
022    import org.eclipse.persistence.logging.JavaLog;
023    import org.eclipse.persistence.logging.SessionLog;
024    import org.eclipse.persistence.logging.SessionLogEntry;
025    import org.eclipse.persistence.sessions.Session;
026    
027    /**
028     * PUBLIC: 
029     * This is a wrapper class for org.apache.commons.logging.Log.
030     * It is used when messages need to be logged through apache commons logging 1.1.
031     *
032     * History :
033     * 
034     * - 05/05/2009 : Updated API to EclipseLink 1.1 version + Javadoc fixes
035     * - 08/05/2009 : Fix for log4j levels in conflict with eclipselink jpa levels (shouldLog)
036     * 
037     * TODO : Use Enum instead of int values for Levels (OO Design)
038     *
039     * Requires :
040     * - Jakarta Commons Logging 1.1 : http://commons.apache.org/logging/
041     *     - commons-logging-1.1.1.jar
042     *
043     * - Log4j 1.2 : http://logging.apache.org/log4j/
044     *     - log4j-1.2.15.jar
045     *
046     *
047     * @author laurent bourges (voparis) : bourges.laurent@gmail.com
048     *
049     * @see AbstractSessionLog
050     * @see JavaLog
051     * @see SessionLogEntry
052     */
053    public final class CommonsLoggingSessionLog extends AbstractSessionLog {
054        //~ Constants --------------------------------------------------------------------------------------------------------
055    
056        /** 
057         * internal debugger FLAG : use System.out
058         */
059        public static final boolean FORCE_INTERNAL_DEBUG = false;
060        /** 
061         * internal debugger FLAG : use a stack trace to find caller class
062         */
063        public static final boolean FORCE_INTERNAL_DEBUG_STACK = false;
064        /** 
065         * internal cache FLAG for LogWrapper's levels
066         */
067        public static final boolean USE_INTERNAL_CACHE = true;
068        /** 
069         * internal apache commons Logging diagnostic FLAG : use System.out
070         */
071        public static final boolean FORCE_APACHE_COMMONS_LOGGING_DIAGNOSTICS = false;
072        /** 
073         * value -1 corresponds to an undefined Level
074         */
075        public static final int UNDEFINED_LEVEL = -1;
076        /**
077         * Stores the default session name in case there is the session name is missing. org.eclipse.persistence
078         * package used by Log4J configuration
079         */
080        public static final String ECLIPSELINK_NAMESPACE = "org.eclipse.persistence";
081        /** 
082         * org.eclipse.persistence.default used by Log4J configuration
083         */
084        public static final String DEFAULT_ECLIPSELINK_NAMESPACE = ECLIPSELINK_NAMESPACE + ".default";
085        /** 
086         * org.eclipse.persistence.session used by Log4J configuration
087         */
088        public static final String SESSION_ECLIPSELINK_NAMESPACE = ECLIPSELINK_NAMESPACE + ".session";
089        /** 
090         * Copied from JavaLog for compatibility issues
091         */
092        public static final String LOGGING_LOCALIZATION_STRING = "org.eclipse.persistence.internal.localization.i18n.LoggingLocalizationResource";
093        /** 
094         * Copied from JavaLog for compatibility issues
095         */
096        public static final String TRACE_LOCALIZATION_STRING = "org.eclipse.persistence.internal.localization.i18n.TraceLocalizationResource";
097        /** 
098         * Stores all the java.util.logging.Levels.  The indexes are EclipseLink logging levels.
099         */
100        public static final Level[] JAVA_LEVELS = new Level[]{
101            Level.ALL, Level.FINEST, Level.FINER, Level.FINE, Level.CONFIG, Level.INFO,
102            Level.WARNING, Level.SEVERE, Level.OFF
103        };
104    
105    
106        static {
107            /* static Initializer to call onInit method */
108            onInit();
109        }
110    
111        //~ Members ----------------------------------------------------------------------------------------------------------
112        /**
113         * formats the EclipseLinkLogRecords. Acts as a static variable but not declared static to avoid classLoader
114         * leaks  (clone does not deep clone this instance)
115         */
116        private final FastLogFormatter LOG_FORMATTER = new FastLogFormatter();
117        /**
118         * Represents the HashMap that stores all the name space strings. The keys are category names.  The values are
119         * namespace strings. Acts as a static variable but not declared static to avoid classLoader leaks (clone does not
120         * deep clone this map). Note : Unsynchronized Map = should be thread-safe !
121         */
122        private final Map<String, String> NAMESPACE_MAP = new HashMap<String, String>(32);
123        /**
124         * LogWrapper instances. Acts as a static variable but not declared static to avoid classLoader leaks (clone
125         * does not deep clone this map) Note : Unsynchronized Map = should be thread-safe !
126         */
127        private final Map<String, LogWrapper> CATEGORY_LOGGERS = new HashMap<String, LogWrapper>(32);
128        /** 
129         * Stores the namespace for session, i.e."org.eclipse.persistence.session.#sessionname#".
130         */
131        private String sessionNameSpace;
132    
133        //~ Constructors -----------------------------------------------------------------------------------------------------
134        /**
135         * PUBLIC:
136         *
137         * CommonsLoggingSessionLog Constructor.
138         * 
139         * Used by :
140         * org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.updateLoggers :
141         * creates an instance for singletonLog and sessionLog
142         *
143         * This adds a root logger for DEFAULT_ECLIPSELINK_NAMESPACE.
144         */
145        public CommonsLoggingSessionLog() {
146            super();
147    
148            if (FORCE_INTERNAL_DEBUG) {
149                debug("CommonsLoggingSessionLog.new : instance : " + this, true);
150            }
151    
152            addLogger(DEFAULT_ECLIPSELINK_NAMESPACE, DEFAULT_ECLIPSELINK_NAMESPACE);
153        }
154    
155        //~ Methods ----------------------------------------------------------------------------------------------------------
156        /**
157         * PUBLIC:
158         *
159         * OnInit method : define system properties for org.apache.commons.logging
160         */
161        public static final void onInit() {
162    
163            if (FORCE_APACHE_COMMONS_LOGGING_DIAGNOSTICS) {
164                /**
165                 * The name (<code>org.apache.commons.logging.diagnostics.dest</code>)
166                 * of the property used to enable internal commons-logging
167                 * diagnostic output, in order to get information on what logging
168                 * implementations are being discovered, what classloaders they
169                 * are loaded through, etc.
170                 * <p>
171                 * If a system property of this name is set then the value is
172                 * assumed to be the name of a file. The special strings
173                 * STDOUT or STDERR (case-sensitive) indicate output to
174                 * System.out and System.err respectively.
175                 * <p>
176                 * Diagnostic logging should be used only to debug problematic
177                 * configurations and should not be set in normal production use.
178                 */
179                System.setProperty("org.apache.commons.logging.diagnostics.dest", "STDOUT");
180            }
181        }
182    
183        /**
184         * PUBLIC:
185         *
186         * onExit method : release all ClassLoader references due to apache commons logging LogFactory
187         *
188         * NOTE : <b>This method must be called in the context of a web application via ServletContextListener.contextDestroyed(ServletContextEvent)</b>
189         *
190         * @see org.apache.commons.logging.LogFactory#release(ClassLoader)
191         */
192        public static final void onExit() {
193            // Classloader unload problem with commons-logging :
194            LogFactory.release(Thread.currentThread().getContextClassLoader());
195        }
196    
197        /**
198         * PUBLIC:  Return the effective log level for the name space extracted from session and category. If a
199         * Logger's level is set to be null then the Logger will use an effective Level that will be obtained by walking up
200         * the parent tree and using the first non-null Level.
201         *
202         * @param category category
203         *
204         * @return the effective level according to the java.util.logging.Levels
205         */
206        @Override
207        public final int getLevel(final String category) {
208            if (FORCE_INTERNAL_DEBUG) {
209                debug("CommonsLoggingSessionLog.getLevel : IN : category : " + category);
210            }
211    
212            final LogWrapper lw = getLogWrapper(category);
213    
214            int l = OFF;
215    
216            if (lw != null) {
217                l = lw.getLevel();
218            }
219    
220            if (FORCE_INTERNAL_DEBUG) {
221                debug("CommonsLoggingSessionLog.getLevel : OUT : category : " + category + " : level : " + getLevelString(l));
222            }
223    
224            return l;
225        }
226    
227        /**
228         * PUBLIC:  Set the log level to a logger with name space extracted from the given category.
229         *
230         * @param level value according to the java.util.logging.Levels
231         * @param category category
232         */
233        @Override
234        public final void setLevel(final int level, final String category) {
235            AccessController.doPrivileged(
236                    new PrivilegedAction<Object>() {
237    
238                        public Object run() {
239                            if (FORCE_INTERNAL_DEBUG) {
240                                debug("CommonsLoggingSessionLog.setLevel : IN : category : " + category + " to level : " +
241                                        getLevelString(level), true);
242                            }
243    
244                            final LogWrapper lw = getLogWrapper(category);
245    
246                            if (lw == null) {
247                                error("CommonsLoggingSessionLog.setLevel : category not found : " + category);
248                            } else {
249                                final Logger logger = getLog4JLogger(lw.getLog());
250    
251                                if (logger == null) {
252                                    error("CommonsLoggingSessionLog.setLevel : Logger not found : " + category + " : " + lw.getLog());
253                                } else {
254    
255                                    final org.apache.log4j.Level realLevel = getLevelFor(level);
256    
257                                    if (realLevel == org.apache.log4j.Level.OFF) {
258                                        // force level to OFF :
259                                        lw.setLevel(OFF);
260                                        error("CommonsLoggingSessionLog.setLevel : unknown level : " + getLevelString(level) + " : level set to OFF");
261                                    } else {
262                                        lw.setLevel(level);
263                                    }
264                                    logger.setLevel(realLevel);
265    
266                                    if (logger.isEnabledFor(org.apache.log4j.Level.WARN)) {
267                                        logger.warn("CommonsLoggingSessionLog.setLevel : Logger Level set to : " + logger.getLevel());
268                                    }
269    
270                                    if (FORCE_INTERNAL_DEBUG) {
271                                        debug("CommonsLoggingSessionLog.setLevel : OUT : category : " + category + " to level : " +
272                                                logger.getLevel());
273                                    }
274                                }
275                            }
276    
277                            // nothing to return :
278                            return null;
279                        }
280                    });
281        }
282    
283        /**
284         * PUBLIC:  Set the output stream that will receive the formatted log entries. DO nothing as Log4J manages the
285         * appenders (console or files) via Log4J configuration
286         *
287         * @param fileOutputStream the file output stream will receive the formatted log entries.
288         */
289        @Override
290        public final void setWriter(final OutputStream fileOutputStream) {
291            if (FORCE_INTERNAL_DEBUG) {
292                debug("CommonsLoggingSessionLog.setWriter : stream : " + fileOutputStream);
293            }
294    
295        // do nothing
296        }
297    
298        /**
299         * PUBLIC: Set the session and session namespace.
300         *
301         * @param pSession an eclipselink Session
302         */
303        @Override
304        public final void setSession(final Session pSession) {
305            super.setSession(pSession);
306    
307            if (pSession != null) {
308                final String sessionName = pSession.getName();
309    
310                if ((sessionName != null) && (sessionName.length() != 0)) {
311                    this.sessionNameSpace = SESSION_ECLIPSELINK_NAMESPACE + "." + sessionName;
312                } else {
313                    this.sessionNameSpace = DEFAULT_ECLIPSELINK_NAMESPACE;
314                }
315    
316                if (FORCE_INTERNAL_DEBUG) {
317                    debug("CommonsLoggingSessionLog.setSession : sessionNameSpace : " + this.sessionNameSpace);
318                }
319    
320                //Initialize loggers eagerly
321                addLogger(this.sessionNameSpace, this.sessionNameSpace);
322    
323                addDefaultLoggers(this.sessionNameSpace);
324            }
325        }
326    
327        /**
328         * PUBLIC: Check if a message of the given lev would actually be logged by the logger with name space built
329         * from the given session and category. Return the shouldLog for the given category Note : this method is very very
330         * used so optimized for performance.
331         *
332         * @param level value according to the java.util.logging.Levels
333         * @param category category
334         *
335         * @return true if the given message will be logged
336         */
337        @Override
338        public final boolean shouldLog(final int level, final String category) {
339            if (FORCE_INTERNAL_DEBUG) {
340                debug("CommonsLoggingSessionLog.shouldLog : IN : category : " + category + " : " + getLevelString(level));
341            }
342    
343            boolean res = false;
344    
345            switch (level) {
346                case OFF:
347                    res = false;
348    
349                    break;
350    
351                case ALL:
352                    res = true;
353    
354                    break;
355    
356                default:
357    
358                    final LogWrapper lw = getLogWrapper(category);
359    
360                    if (lw == null) {
361                        error("CommonsLoggingSessionLog.shouldLog : category : " + category + " - NO LOGGER FOUND");
362                        res = false;
363                    } else {
364                        res = level >= lw.getLevel();
365                    }
366            }
367    
368            if (FORCE_INTERNAL_DEBUG) {
369                debug("CommonsLoggingSessionLog.shouldLog : OUT : category : " + category + " : " + res);
370            }
371    
372            return res;
373        }
374    
375        /**
376         * PUBLIC: Log a SessionLogEntry
377         *
378         * @param entry SessionLogEntry that holds all the information for a EclipseLink logging event
379         */
380        public final void log(final SessionLogEntry entry) {
381            if (shouldLog(entry.getLevel(), entry.getNameSpace())) {
382                if (FORCE_INTERNAL_DEBUG) {
383                    debug("CommonsLoggingSessionLog.log : namespace : " + entry.getNameSpace() + " message : " + entry.getMessage());
384                }
385    
386                final Log log = getLog(entry.getNameSpace());
387    
388                if (log == null) {
389                    error("CommonsLoggingSessionLog.log : no Log found for : " + entry.getNameSpace());
390                } else {
391                    final Level javaLevel = getJavaLevel(entry.getLevel());
392    
393                    internalLog(entry, javaLevel, log);
394                }
395            }
396        }
397    
398        /**
399         * PUBLIC: Log a throwable.
400         *
401         * @param throwable a throwable
402         */
403        @Override
404        public final void throwing(final Throwable throwable) {
405            final Log log = getLog(null);
406    
407            if (log != null) {
408                log.error(null, throwable);
409            }
410        }
411    
412        /**
413         * INTERNAL: Each session owns its own session log because session is stored in the session log
414         *
415         * @return value TODO : Value Description
416         */
417        @Override
418        public final Object clone() {
419            // There is no special treatment required for cloning here
420            // The state of this object is described  by member variables sessionLogger and categoryLoggers.
421            // This state depends on session.
422            // If session for the clone is going to be the same as session for this there is no
423            // need to do "deep" cloning.
424            // If not, the session being cloned should call setSession() on its JavaLog object to initialize it correctly.
425            final CommonsLoggingSessionLog cloneLog = (CommonsLoggingSessionLog) super.clone();
426    
427            if (FORCE_INTERNAL_DEBUG) {
428                debug("CommonsLoggingSessionLog.clone : cloned instance : " + cloneLog, true);
429            }
430    
431            return cloneLog;
432        }
433    
434        /**
435         * INTERNAL: Add Logger to the categoryloggers.
436         *
437         * @param loggerCategory category
438         * @param loggerNameSpace name space
439         */
440        private final void addLogger(final String loggerCategory, final String loggerNameSpace) {
441            if (FORCE_INTERNAL_DEBUG) {
442                debug("CommonsLoggingSessionLog.addLogger : category : " + loggerCategory + " in name space : " + loggerNameSpace);
443            }
444    
445            this.CATEGORY_LOGGERS.put(loggerCategory, new LogWrapper(this, loggerCategory, LogFactory.getLog(loggerNameSpace)));
446        }
447    
448        /**
449         * INTERNAL: Return the name space for the given category from the map.
450         *
451         * @param category category
452         *
453         * @return name space for the given category
454         */
455        private final String getNameSpaceString(final String category) {
456            if (getSession() == null) {
457                return DEFAULT_ECLIPSELINK_NAMESPACE;
458            } else if ((category == null) || (category.length() == 0)) {
459                return this.sessionNameSpace;
460            } else {
461                return this.NAMESPACE_MAP.get(category);
462            }
463        }
464    
465        /**
466         * INTERNAL: Return the LogWrapper instance for the given category Note : this method is very very used so
467         * optimized for performance.
468         *
469         * @param category category
470         *
471         * @return LogWrapper instance or null if not found
472         */
473        private final LogWrapper getLogWrapper(final String category) {
474            LogWrapper lw = null;
475    
476            if (getSession() == null) {
477                if (FORCE_INTERNAL_DEBUG) {
478                    debug("CommonsLoggingSessionLog.getLogWrapper : " + category + " : SESSION NULL");
479                }
480    
481                lw = this.CATEGORY_LOGGERS.get(DEFAULT_ECLIPSELINK_NAMESPACE);
482            } else {
483                lw = this.CATEGORY_LOGGERS.get(category);
484    
485                if (lw == null) {
486                    if (FORCE_INTERNAL_DEBUG) {
487                        debug("CommonsLoggingSessionLog.getLogWrapper : " + category + " : CATEGORY IS NULL ?");
488                    }
489    
490                    // really few cases :
491                    if ((category == null) || (category.length() == 0)) {
492                        lw = this.CATEGORY_LOGGERS.get(this.sessionNameSpace);
493                    }
494                }
495            }
496    
497            if (FORCE_INTERNAL_DEBUG) {
498                debug("CommonsLoggingSessionLog.getLogWrapper : " + category + " = " + lw);
499            }
500    
501            return lw;
502        }
503    
504        /**
505         * INTERNAL: Return the apache commons logging Log instance for the given category
506         *
507         * @param category category
508         *
509         * @return value Log instance or null if not found
510         */
511        private final Log getLog(final String category) {
512            if (FORCE_INTERNAL_DEBUG) {
513                debug("CommonsLoggingSessionLog.getLogger : IN : category : " + category);
514            }
515    
516            final LogWrapper lw = getLogWrapper(category);
517    
518            Log log = null;
519    
520            if (lw != null) {
521                log = lw.getLog();
522            }
523    
524            if (FORCE_INTERNAL_DEBUG) {
525                debug("CommonsLoggingSessionLog.getLogger : OUT : log : " + log);
526            }
527    
528            return log;
529        }
530    
531        /**
532         * INTERNAL: Adds default loggers for the given name space
533         *
534         * @param namespace name space
535         */
536        private final void addDefaultLoggers(final String namespace) {
537            if (FORCE_INTERNAL_DEBUG) {
538                debug("CommonsLoggingSessionLog.addDefaultLoggers : nameSpace : " + namespace);
539            }
540    
541            String loggerCategory;
542            String loggerNameSpace;
543    
544            final String[] categories = SessionLog.loggerCatagories;
545    
546            final int size = categories.length;
547    
548            for (int i = 0; i < size; i++) {
549                loggerCategory = categories[i];
550                loggerNameSpace = namespace + "." + loggerCategory;
551    
552                this.NAMESPACE_MAP.put(loggerCategory, loggerNameSpace);
553                addLogger(loggerCategory, loggerNameSpace);
554            }
555    
556            if (FORCE_INTERNAL_DEBUG) {
557                debug("CommonsLoggingSessionLog.addDefaultLoggers : NAMESPACE_MAP : " + this.NAMESPACE_MAP);
558                debug("CommonsLoggingSessionLog.addDefaultLoggers : CATEGORY_LOGGERS : " + this.CATEGORY_LOGGERS);
559            }
560        }
561    
562        /**
563         * INTERNAL:  Build a LogRecord
564         *
565         * @param entry SessionLogEntry that holds all the information for a EclipseLink logging event
566         * @param level according to eclipselink level
567         * @param log commons-logging 1.1 wrapper
568         */
569        private final void internalLog(final SessionLogEntry entry, final Level level, final Log log) {
570            if (FORCE_INTERNAL_DEBUG) {
571                debug("CommonsLoggingSessionLog.internalLog : " + computeMessage(entry, level));
572            }
573    
574            final int entryLevel = entry.getLevel();
575    
576            switch (entryLevel) {
577                case SEVERE:
578                    log.error(computeMessage(entry, level));
579    
580                    break;
581    
582                case WARNING:
583                    log.warn(computeMessage(entry, level));
584    
585                    break;
586    
587                case INFO:
588                case CONFIG:
589                    log.info(computeMessage(entry, level));
590    
591                    break;
592    
593                case FINE:
594                case FINER:
595                case FINEST:
596                    log.debug(computeMessage(entry, level));
597    
598                    break;
599    
600                case ALL:
601                    log.trace(computeMessage(entry, level));
602    
603                    break;
604    
605                case OFF:
606                    break;
607    
608                default:
609                    error("CommonsLoggingSessionLog.internalLog : unknown level : " + entryLevel);
610    
611                    break;
612            }
613        }
614    
615        /**
616         * INTERNAL: Computes the log message
617         *
618         * @param entry SessionLogEntry that holds all the information for a EclipseLink logging event
619         * @param level according to eclipselink level
620         *
621         * @return value TODO : Value Description
622         */
623        private final String computeMessage(final SessionLogEntry entry, final Level level) {
624            // Format message so that we do not depend on the bundle
625            final EclipseLinkLogRecord lr = new EclipseLinkLogRecord(level, formatMessage(entry));
626    
627            lr.setSourceClassName(null);
628            lr.setSourceMethodName(null);
629            lr.setLoggerName(getNameSpaceString(entry.getNameSpace()));
630    
631            if (shouldPrintSession()) {
632                lr.setSessionString(getSessionString(entry.getSession()));
633            }
634    
635            if (shouldPrintConnection()) {
636                lr.setConnection(entry.getConnection());
637            }
638    
639            lr.setThrown(entry.getException());
640            lr.setShouldLogExceptionStackTrace(shouldLogExceptionStackTrace());
641    
642            lr.setShouldPrintDate(shouldPrintDate());
643            lr.setShouldPrintThread(shouldPrintThread());
644    
645            return this.LOG_FORMATTER.format(lr);
646        }
647    
648        /**
649         * INTERNAL: Return the corresponding java.util.logging.Level for a given eclipselink level.
650         *
651         * @param level according to eclipselink level
652         *
653         * @return value according to the java.util.logging.Levels
654         */
655        private static final Level getJavaLevel(final int level) {
656            return JAVA_LEVELS[level];
657        }
658    
659        /**
660         * INTERNAL: Returns Log4JLogger instance
661         *
662         * @param log commons-logging 1.1 wrapper
663         *
664         * @return Log4JLogger instance
665         */
666        private static final Logger getLog4JLogger(final Log log) {
667            Logger l = null;
668    
669            if (log instanceof Log4JLogger) {
670                l = ((Log4JLogger) log).getLogger();
671            }
672    
673            if (FORCE_INTERNAL_DEBUG) {
674                debug("CommonsLoggingSessionLog.getLog4JLogger : " + l);
675            }
676    
677            return l;
678        }
679    
680        /**
681         * PUBLIC: SHOULD BE in AbstractSessionLog
682         *
683         * @see AbstractSessionLog#getLevelString() Return the log level as a string value.
684         */
685        private static final String getLevelString(final int level) {
686            switch (level) {
687                case OFF:
688                    return "OFF";
689    
690                case SEVERE:
691                    return "SEVERE";
692    
693                case WARNING:
694                    return "WARNING";
695    
696                case INFO:
697                    return "INFO";
698    
699                case CONFIG:
700                    return "CONFIG";
701    
702                case FINE:
703                    return "FINE";
704    
705                case FINER:
706                    return "FINER";
707    
708                case FINEST:
709                    return "FINEST";
710    
711                case ALL:
712                    return "ALL";
713    
714                default:
715                    return "INFO";
716            }
717        }
718    
719        /**
720         * Returns the real Log4J Level 
721         * @param level eclipselink level
722         * @return org.apache.log4j.Level
723         */
724        private static final org.apache.log4j.Level getLevelFor(final int level) {
725            org.apache.log4j.Level realLevel = null;
726            switch (level) {
727                case SEVERE:
728                    realLevel = org.apache.log4j.Level.ERROR;
729    
730                    break;
731    
732                case WARNING:
733                    realLevel = org.apache.log4j.Level.WARN;
734    
735                    break;
736    
737                case INFO:
738                case CONFIG:
739                    realLevel = org.apache.log4j.Level.INFO;
740    
741                    break;
742    
743                case FINE:
744                case FINER:
745                case FINEST:
746                    realLevel = org.apache.log4j.Level.DEBUG;
747    
748                    break;
749    
750                case ALL:
751                    realLevel = org.apache.log4j.Level.ALL;
752    
753                    break;
754    
755                case OFF:
756                    realLevel = org.apache.log4j.Level.OFF;
757    
758                    break;
759    
760                default:
761                    realLevel = org.apache.log4j.Level.OFF;
762                    error("CommonsLoggingSessionLog.getLevelFor : unknown level : " + getLevelString(level) + " = OFF");
763    
764                    break;
765            }
766            if (FORCE_INTERNAL_DEBUG) {
767                debug("CommonsLoggingSessionLog.getLevelFor : level : " + getLevelString(level) + " = " + realLevel);
768            }
769            return realLevel;
770    
771        }
772    
773        /**
774         * Prints the message in Std out
775         * @param message message to print
776         */
777        protected final static void debug(final String message) {
778            debug(message, false);
779        }
780    
781        /**
782         * Prints the message in Std out
783         * @param message message to print
784         * @param printStack adds a stack trace to find caller class
785         */
786        protected final static void debug(final String message, final boolean printStack) {
787            System.out.println(message);
788    //        if (printStack && FORCE_INTERNAL_DEBUG_STACK) {
789    //            // to inspect the calling stack :
790    //            new Throwable().printStackTrace(System.out);
791    //        }
792        }
793    
794        /**
795         * Prints the message in Std err
796         * @param message message to print
797         */
798        protected final static void error(final String message) {
799            System.err.println(message);
800        }
801    
802        /**
803         * Prints the message in Std err
804         * @param message message to print
805         */
806        protected final static void error(final Throwable th) {
807            th.printStackTrace(System.err);
808        }
809    
810    //~ Inner Classes ----------------------------------------------------------------------------------------------------
811        /**
812         * INTERNAL: LogWrapper class wraps the real apache commons logging Log instance
813         */
814        private static final class LogWrapper {
815            //~ Members --------------------------------------------------------------------------------------------------------
816    
817            /** parent CommonsLoggingSessionLog instance */
818            private final CommonsLoggingSessionLog sessionLog;
819            /** category for debug mode */
820            private final String category;
821            /** apache commons logging Log instance */
822            private final Log log;
823            /** parent LogWrapper */
824            private final LogWrapper parent;
825            /** child LogWrapper instances */
826            private List<LogWrapper> children = null;
827            /** level as defined by java.util.logging.Levels. Can be changed at runtime */
828            private int level = UNDEFINED_LEVEL;
829            /** cached level as defined by java.util.logging.Levels. Extracted from parent LogWrapper instances */
830            private int cachedLevel = UNDEFINED_LEVEL;
831    
832            //~ Constructors ---------------------------------------------------------------------------------------------------
833            /**
834             * INTERNAL:
835             *
836             * Constructor
837             *
838             * @param log apache commons logging Log instance
839             */
840            protected LogWrapper(final CommonsLoggingSessionLog sessionLog, final String category, final Log log) {
841                this.sessionLog = sessionLog;
842                this.category = category;
843                this.log = log;
844    
845                final Logger logger = CommonsLoggingSessionLog.getLog4JLogger(log);
846    
847                String parentName = null;
848    
849                if (logger != null) {
850                    parentName = logger.getParent().getName();
851                }
852    
853                if ((parentName != null) && !"null".equals(parentName)) {
854                    if (FORCE_INTERNAL_DEBUG) {
855                        debug("CommonsLoggingSessionLog.LogWrapper.new : parent : " + parentName);
856                    }
857    
858                    this.parent = this.sessionLog.getLogWrapper(parentName);
859    
860                    if (this.parent != null) {
861                        this.parent.addChild(this);
862                    }
863                } else {
864                    this.parent = null;
865                }
866            }
867    
868            //~ Methods --------------------------------------------------------------------------------------------------------
869            /**
870             * INTERNAL: Returns the category
871             *
872             * @return category
873             */
874            protected final String getCategory() {
875                return this.category;
876            }
877    
878            /**
879             * INTERNAL: Reset the cachedLevel
880             */
881            protected final void resetCachedLevel() {
882                this.cachedLevel = UNDEFINED_LEVEL;
883    
884                if (FORCE_INTERNAL_DEBUG) {
885                    debug("CommonsLoggingSessionLog.LogWrapper.setLevel : reset cachedLevel for : " + getCategory());
886                }
887            }
888    
889            /**
890             * INTERNAL: Returns the apache commons logging Log instance
891             *
892             * @return apache commons logging Log instance
893             */
894            protected final Log getLog() {
895                return this.log;
896            }
897    
898            /**
899             * INTERNAL: Returns the level according to the java.util.logging.Levels
900             *
901             * @return level computed from cachedLevel and internal level
902             */
903            protected final int getLevel() {
904                int res = this.cachedLevel;
905    
906                // if the cachedLevel is undefined : compute it from parents :
907                if (res == UNDEFINED_LEVEL) {
908                    // first gives the internal level :
909                    res = this.level;
910    
911                    if (FORCE_INTERNAL_DEBUG) {
912                        System.out.println(
913                                "CommonsLoggingSessionLog.LogWrapper.getLevel : this : " + getCategory() + " : UNCACHED level : " + getLevelString(res));
914                    }
915    
916                    if (res == UNDEFINED_LEVEL) {
917                        res = computeLevel(UNDEFINED_LEVEL);
918    
919                        if (USE_INTERNAL_CACHE) {
920                            this.cachedLevel = res;
921                        }
922                    }
923                }
924                return res;
925            }
926    
927            /**
928             * INTERNAL: Defines the level according to the java.util.logging.Levels
929             *
930             * @param level value according to the java.util.logging.Levels
931             */
932            protected final void setLevel(final int level) {
933                this.level = level;
934    
935                if (USE_INTERNAL_CACHE) {
936                    this.resetCachedLevel();
937                    if (this.children != null) {
938                        // reset cachedLevel for all children :
939                        for (final LogWrapper cw : this.children) {
940                            cw.resetCachedLevel();
941                        }
942                    }
943                }
944            }
945    
946            /**
947             * INTERNAL: Adds a child LogWrapper
948             *
949             * @param lw
950             */
951            protected final void addChild(final LogWrapper lw) {
952                if (this.children == null) {
953                    this.children = new ArrayList<LogWrapper>();
954                }
955    
956                this.children.add(lw);
957    
958                if (FORCE_INTERNAL_DEBUG) {
959                    System.out.println(
960                            "CommonsLoggingSessionLog.LogWrapper.addChild : this : " + getCategory() + " : child : " + lw.getCategory());
961                }
962            }
963    
964            /**
965             * INTERNAL: Computes the cached Level
966             *
967             * @param localLevel this.level copy
968             *
969             * @return level
970             */
971            private final int computeLevel(final int localLevel) {
972                LogWrapper pw;
973                LogWrapper lw = this;
974    
975                if (FORCE_INTERNAL_DEBUG) {
976                    System.out.println(
977                            "CommonsLoggingSessionLog.LogWrapper.computeLevel : IN : " + getCategory() + " : level : " +
978                            CommonsLoggingSessionLog.getLevelString(localLevel));
979                }
980    
981                while ((lw != null) &&
982                        (((lw == this) && (localLevel == UNDEFINED_LEVEL)) ||
983                        ((lw != this) && (lw.getLevel() == UNDEFINED_LEVEL)))) {
984                    pw = lw.parent;
985    
986                    if (pw != lw) {
987                        lw = pw;
988                    } else {
989                        // exit from loop :
990                        lw = null;
991                    }
992                }
993    
994                int computedLevel = OFF;
995    
996                if (lw != null) {
997                    computedLevel = lw.getLevel();
998    
999                    if (FORCE_INTERNAL_DEBUG) {
1000                        System.out.println(
1001                                "CommonsLoggingSessionLog.LogWrapper.computeLevel : category : " + lw.getCategory() + " - computedLevel : " + getLevelString(computedLevel) + " : " + lw.getLog());
1002                    }
1003                    final Logger logger = getLog4JLogger(lw.getLog());
1004    
1005                    if (logger == null) {
1006                        error("CommonsLoggingSessionLog.computeLevel : Logger not found : " + lw.getCategory() + " : " + lw.getLog());
1007                    } else {
1008    
1009                        final org.apache.log4j.Level realLevel = getLevelFor(computedLevel);
1010    
1011                        final boolean enabled = logger.isEnabledFor(realLevel);
1012    
1013                        if (!enabled) {
1014                            computedLevel = OFF;
1015                        }
1016                        if (FORCE_INTERNAL_DEBUG) {
1017                            System.out.println(
1018                                    "CommonsLoggingSessionLog.LogWrapper.computeLevel : category : " + lw.getCategory() + " :  realLevel : " + realLevel + " - enabled = " + enabled);
1019                        }
1020                    }
1021    
1022    
1023                }
1024    
1025                if (FORCE_INTERNAL_DEBUG) {
1026                    System.out.println(
1027                            "CommonsLoggingSessionLog.LogWrapper.computeLevel : OUT : " + getCategory() + " : level : " +
1028                            CommonsLoggingSessionLog.getLevelString(computedLevel));
1029                }
1030    
1031                return computedLevel;
1032            }
1033        }
1034    }
1035    //~ End of file --------------------------------------------------------------------------------------------------------