View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.log4j;
19  
20  import org.apache.log4j.helpers.LogLog;
21  import org.apache.log4j.or.ObjectRenderer;
22  import org.apache.log4j.or.RendererMap;
23  import org.apache.log4j.plugins.Plugin;
24  import org.apache.log4j.plugins.PluginRegistry;
25  import org.apache.log4j.scheduler.Scheduler;
26  import org.apache.log4j.spi.ErrorItem;
27  import org.apache.log4j.spi.HierarchyEventListener;
28  import org.apache.log4j.spi.LoggerEventListener;
29  import org.apache.log4j.spi.LoggerFactory;
30  import org.apache.log4j.spi.LoggerRepository;
31  import org.apache.log4j.spi.LoggerRepositoryEventListener;
32  import org.apache.log4j.spi.LoggerRepositoryEx;
33  import org.apache.log4j.spi.RendererSupport;
34  import org.apache.log4j.xml.UnrecognizedElementHandler;
35  import org.apache.log4j.xml.DOMConfigurator;
36  import org.w3c.dom.Element;
37  
38  import java.util.ArrayList;
39  import java.util.Enumeration;
40  import java.util.HashMap;
41  import java.util.Hashtable;
42  import java.util.List;
43  import java.util.Map;
44  import java.util.Properties;
45  import java.util.Vector;
46  
47  
48  /**
49   * This class implements LoggerRepositoryEx by
50   *   wrapping an existing LoggerRepository implementation
51   *   and implementing the newly added capabilities.
52  */
53  public final class LoggerRepositoryExImpl
54          implements LoggerRepositoryEx,
55          RendererSupport,
56          UnrecognizedElementHandler {
57  
58      /**
59       * Wrapped logger repository.
60       */
61    private final LoggerRepository repo;
62  
63      /**
64       * Logger factory.  Does not affect class of logger
65       * created by underlying repository.
66       */
67    private LoggerFactory loggerFactory;
68  
69      /**
70       *  Renderer support.
71       */
72    private final RendererSupport rendererSupport;
73  
74      /**
75       * List of repository event listeners.
76       */
77    private final ArrayList repositoryEventListeners = new ArrayList();
78      /**
79       * Map of HierarchyEventListener keyed by LoggingEventListener.
80       */
81    private final Map loggerEventListeners = new HashMap();
82      /**
83       * Name of hierarchy.
84       */
85    private String name;
86      /**
87       * Plug in registry.
88       */
89    private PluginRegistry pluginRegistry;
90      /**
91       * Properties.
92       */
93    private final Map properties = new Hashtable();
94      /**
95       * Scheduler.
96       */
97    private Scheduler scheduler;
98  
99    /** The repository can also be used as an object store
100    * for various objects used by log4j components.
101    */
102   private Map objectMap = new HashMap();
103 
104 
105     /**
106      * Error list.
107      */
108   private List errorList = new Vector();
109 
110     /**
111      * True if hierarchy has not been modified.
112      */
113   private boolean pristine = true;
114 
115   /**
116      Constructs a new logger hierarchy.
117 
118      @param repository Base implementation of repository.
119 
120    */
121   public LoggerRepositoryExImpl(final LoggerRepository repository) {
122     super();
123     if (repository == null) {
124         throw new NullPointerException("repository");
125     }
126     repo = repository;
127     if (repository instanceof RendererSupport) {
128         rendererSupport = (RendererSupport) repository;
129     } else {
130         rendererSupport = new RendererSupportImpl();
131     }
132   }
133 
134 
135   /**
136     Add a {@link LoggerRepositoryEventListener} to the repository. The
137     listener will be called when repository events occur.
138     @param listener listener
139     */
140   public void addLoggerRepositoryEventListener(
141     final LoggerRepositoryEventListener listener) {
142     synchronized (repositoryEventListeners) {
143       if (repositoryEventListeners.contains(listener)) {
144         LogLog.warn(
145           "Ignoring attempt to add a previously "
146                   + "registered LoggerRepositoryEventListener.");
147       } else {
148         repositoryEventListeners.add(listener);
149       }
150     }
151   }
152 
153 
154   /**
155     Remove a {@link LoggerRepositoryEventListener} from the repository.
156     @param listener listener
157     */
158   public void removeLoggerRepositoryEventListener(
159     final LoggerRepositoryEventListener listener) {
160     synchronized (repositoryEventListeners) {
161       if (!repositoryEventListeners.contains(listener)) {
162         LogLog.warn(
163           "Ignoring attempt to remove a "
164                   + "non-registered LoggerRepositoryEventListener.");
165       } else {
166         repositoryEventListeners.remove(listener);
167       }
168     }
169   }
170 
171   /**
172     Add a {@link LoggerEventListener} to the repository. The  listener
173     will be called when repository events occur.
174     @param listener listener
175    */
176   public void addLoggerEventListener(final LoggerEventListener listener) {
177     synchronized (loggerEventListeners) {
178       if (loggerEventListeners.get(listener) != null) {
179         LogLog.warn(
180          "Ignoring attempt to add a previously registerd LoggerEventListener.");
181       } else {
182         HierarchyEventListenerProxy proxy =
183                 new HierarchyEventListenerProxy(listener);
184         loggerEventListeners.put(listener, proxy);
185         repo.addHierarchyEventListener(proxy);
186       }
187     }
188   }
189 
190     /**
191        Add a {@link org.apache.log4j.spi.HierarchyEventListener}
192      event to the repository.
193      @param listener listener
194        @deprecated Superceded by addLoggerEventListener
195     */
196     public
197     void addHierarchyEventListener(final HierarchyEventListener listener) {
198         repo.addHierarchyEventListener(listener);
199     }
200 
201 
202   /**
203     Remove a {@link LoggerEventListener} from the repository.
204     @param listener listener to be removed
205     */
206   public void removeLoggerEventListener(final LoggerEventListener listener) {
207     synchronized (loggerEventListeners) {
208       HierarchyEventListenerProxy proxy =
209               (HierarchyEventListenerProxy) loggerEventListeners.get(listener);
210       if (proxy == null) {
211         LogLog.warn(
212           "Ignoring attempt to remove a non-registered LoggerEventListener.");
213       } else {
214         loggerEventListeners.remove(listener);
215         proxy.disable();
216       }
217     }
218   }
219 
220     /**
221      * Issue warning that there are no appenders in hierarchy.
222      * @param cat logger, not currently used.
223      */
224   public void emitNoAppenderWarning(final Category cat) {
225     repo.emitNoAppenderWarning(cat);
226   }
227 
228   /**
229      Check if the named logger exists in the hierarchy. If so return
230      its reference, otherwise returns <code>null</code>.
231 
232      @param loggerName The name of the logger to search for.
233      @return true if logger exists.
234   */
235   public Logger exists(final String loggerName) {
236     return repo.exists(loggerName);
237   }
238 
239   /**
240    * Return the name of this hierarchy.
241    * @return name of hierarchy
242    */
243   public String getName() {
244     return name;
245   }
246 
247   /**
248    * Set the name of this repository.
249    *
250    * Note that once named, a repository cannot be rerenamed.
251    * @param repoName name of hierarchy
252    */
253   public void setName(final String repoName) {
254     if (name == null) {
255       name = repoName;
256     } else if (!name.equals(repoName)) {
257       throw new IllegalStateException(
258         "Repository [" + name + "] cannot be renamed as [" + repoName + "].");
259     }
260   }
261 
262   /**
263    * {@inheritDoc}
264    */
265   public Map getProperties() {
266     return properties;
267   }
268 
269   /**
270    * {@inheritDoc}
271    */
272   public String getProperty(final String key) {
273      return (String) properties.get(key);
274   }
275 
276   /**
277    * Set a property by key and value. The property will be shared by all
278    * events in this repository.
279    * @param key property name
280    * @param value property value
281    */
282   public void setProperty(final String key,
283                           final String value) {
284    properties.put(key, value);
285   }
286 
287   /**
288      The string form of {@link #setThreshold(Level)}.
289    @param levelStr symbolic name for level
290   */
291   public void setThreshold(final String levelStr) {
292     repo.setThreshold(levelStr);
293   }
294 
295   /**
296      Enable logging for logging requests with level <code>l</code> or
297      higher. By default all levels are enabled.
298 
299      @param l The minimum level for which logging requests are sent to
300      their appenders.  */
301   public void setThreshold(final Level l) {
302     repo.setThreshold(l);
303   }
304 
305   /**
306    * {@inheritDoc}
307    */
308   public PluginRegistry getPluginRegistry() {
309    if (pluginRegistry == null) {
310      pluginRegistry = new PluginRegistry(this);
311    }
312    return pluginRegistry;
313   }
314 
315 
316     /**
317       Requests that a appender added event be sent to any registered
318       {@link LoggerEventListener}.
319       @param logger The logger to which the appender was added.
320       @param appender The appender added to the logger.
321      */
322     public void fireAddAppenderEvent(final Category logger,
323                                      final Appender appender) {
324         repo.fireAddAppenderEvent(logger, appender);
325     }
326 
327 
328     /**
329       Requests that a appender removed event be sent to any registered
330       {@link LoggerEventListener}.
331       @param logger The logger from which the appender was removed.
332       @param appender The appender removed from the logger.
333       */
334     public void fireRemoveAppenderEvent(final Category logger,
335                                         final Appender appender) {
336        if (repo instanceof Hierarchy) {
337            ((Hierarchy) repo).fireRemoveAppenderEvent(logger, appender);
338        }
339     }
340 
341 
342   /**
343     Requests that a level changed event be sent to any registered
344     {@link LoggerEventListener}.
345     @param logger The logger which changed levels.
346     */
347   public void fireLevelChangedEvent(final Logger logger) {
348   }
349 
350   /**
351    *
352    * Requests that a configuration changed event be sent to any registered
353    * {@link LoggerRepositoryEventListener}.
354    * 
355    */
356   public void fireConfigurationChangedEvent() {
357   }
358 
359 
360   /**
361      Returns the current threshold.
362      @return current threshold level
363 
364      @since 1.2 */
365   public Level getThreshold() {
366     return repo.getThreshold();
367   }
368 
369 
370   /**
371      Return a new logger instance named as the first parameter using
372      the default factory.
373 
374      <p>If a logger of that name already exists, then it will be
375      returned.  Otherwise, a new logger will be instantiated and
376      then linked with its existing ancestors as well as children.
377 
378      @param loggerName The name of the logger to retrieve.
379      @return logger
380 
381   */
382   public Logger getLogger(final String loggerName) {
383     return repo.getLogger(loggerName);
384   }
385 
386   /**
387       Return a new logger instance named as the first parameter using
388       <code>factory</code>.
389 
390       <p>If a logger of that name already exists, then it will be
391       returned.  Otherwise, a new logger will be instantiated by the
392       <code>factory</code> parameter and linked with its existing
393       ancestors as well as children.
394 
395       @param loggerName The name of the logger to retrieve.
396       @param factory The factory that will make the new logger instance.
397       @return logger
398 
399   */
400   public Logger getLogger(final String loggerName,
401                           final LoggerFactory factory) {
402     return repo.getLogger(loggerName, factory);
403   }
404 
405   /**
406      Returns all the currently defined categories in this hierarchy as
407      an {@link java.util.Enumeration Enumeration}.
408 
409      <p>The root logger is <em>not</em> included in the returned
410      {@link Enumeration}.
411      @return enumerator of current loggers
412    */
413   public Enumeration getCurrentLoggers() {
414     return repo.getCurrentLoggers();
415   }
416 
417   /**
418    * Return the the list of previously encoutered {@link ErrorItem error items}.
419    * @return list of errors
420    */
421   public List getErrorList() {
422     return errorList;
423   }
424 
425   /**
426    * Add an error item to the list of previously encountered errors.
427    * @param errorItem error to add to list of errors.
428    */
429   public void addErrorItem(final ErrorItem errorItem) {
430     getErrorList().add(errorItem);
431   }
432 
433   /**
434    * Get enumerator over current loggers.
435    * @return enumerator over current loggers
436      @deprecated Please use {@link #getCurrentLoggers} instead.
437    */
438   public Enumeration getCurrentCategories() {
439     return repo.getCurrentCategories();
440   }
441 
442   /**
443      Get the renderer map for this hierarchy.
444    @return renderer map
445   */
446   public RendererMap getRendererMap() {
447     return rendererSupport.getRendererMap();
448   }
449 
450   /**
451      Get the root of this hierarchy.
452 
453      @since 0.9.0
454    @return root of hierarchy
455    */
456   public Logger getRootLogger() {
457     return repo.getRootLogger();
458   }
459 
460   /**
461      This method will return <code>true</code> if this repository is
462      disabled for <code>level</code> value passed as parameter and
463      <code>false</code> otherwise. See also the {@link
464      #setThreshold(Level) threshold} method.
465    @param level numeric value for level.
466    @return true if disabled for specified level
467    */
468   public boolean isDisabled(final int level) {
469     return repo.isDisabled(level);
470   }
471 
472   /**
473      Reset all values contained in this hierarchy instance to their
474      default.  This removes all appenders from all categories, sets
475      the level of all non-root categories to <code>null</code>,
476      sets their additivity flag to <code>true</code> and sets the level
477      of the root logger to DEBUG.  Moreover,
478      message disabling is set its default "off" value.
479 
480      <p>Existing categories are not removed. They are just reset.
481 
482      <p>This method should be used sparingly and with care as it will
483      block all logging until it is completed.</p>
484 
485      @since 0.8.5 */
486   public void resetConfiguration() {
487     repo.resetConfiguration();
488   }
489 
490   /**
491      Used by subclasses to add a renderer to the hierarchy passed as parameter.
492    @param renderedClass class
493    @param renderer object used to render class.
494    */
495   public void setRenderer(final Class renderedClass,
496                           final ObjectRenderer renderer) {
497     rendererSupport.setRenderer(renderedClass, renderer);
498   }
499 
500   /**
501    * {@inheritDoc}
502    */
503   public boolean isPristine() {
504     return pristine;
505   }
506 
507   /**
508    * {@inheritDoc}
509    */
510   public void setPristine(final boolean state) {
511     pristine = state;
512   }
513 
514   /**
515      Shutting down a hierarchy will <em>safely</em> close and remove
516      all appenders in all categories including the root logger.
517 
518      <p>Some appenders such as org.apache.log4j.net.SocketAppender
519      and AsyncAppender need to be closed before the
520      application exists. Otherwise, pending logging events might be
521      lost.
522 
523      <p>The <code>shutdown</code> method is careful to close nested
524      appenders before closing regular appenders. This is allows
525      configurations where a regular appender is attached to a logger
526      and again to a nested appender.
527 
528      @since 1.0 */
529   public void shutdown() {
530     repo.shutdown();
531   }
532 
533 
534   /**
535    * Return this repository's own scheduler.
536    * The scheduler is lazily instantiated.
537    * @return this repository's own scheduler.
538    */
539   public Scheduler getScheduler() {
540     if (scheduler == null) {
541       scheduler = new Scheduler();
542       scheduler.setDaemon(true);
543       scheduler.start();
544     }
545     return scheduler;
546   }
547 
548     /**
549      * Puts object by key.
550      * @param key key, may not be null.
551      * @param value object to associate with key.
552      */
553   public void putObject(final String key,
554                         final Object value) {
555     objectMap.put(key, value);
556   }
557 
558     /**
559      * Get object by key.
560      * @param key key, may not be null.
561      * @return object associated with key or null.
562      */
563   public Object getObject(final String key) {
564     return objectMap.get(key);
565   }
566 
567     /**
568      * Set logger factory.
569      * @param factory logger factory.
570      */
571   public void setLoggerFactory(final LoggerFactory factory) {
572     if (factory == null) {
573       throw new NullPointerException();
574     }
575     this.loggerFactory = factory;
576   }
577 
578     /**
579      * Get logger factory.
580      * @return logger factory.
581      */
582   public LoggerFactory getLoggerFactory() {
583     return loggerFactory;
584   }
585 
586     /** {@inheritDoc} */
587     public boolean parseUnrecognizedElement(
588             final Element element,
589             final Properties props) throws Exception {
590         if ("plugin".equals(element.getNodeName())) {
591             Object instance =
592                     DOMConfigurator.parseElement(element, props, Plugin.class);
593             if (instance instanceof Plugin) {
594                 Plugin plugin = (Plugin) instance;
595                 String pluginName = DOMConfigurator.subst(element.getAttribute("name"), props);
596                 if (pluginName.length() > 0) {
597                     plugin.setName(pluginName);
598                 }
599                 getPluginRegistry().addPlugin(plugin);
600                 plugin.setLoggerRepository(this);
601 
602                 LogLog.debug("Pushing plugin on to the object stack.");
603                 plugin.activateOptions();
604                 return true;
605             }
606         }
607         return false;
608     }
609 
610 
611 
612     /**
613      * Implementation of RendererSupportImpl if not
614      * provided by LoggerRepository.
615      */
616    private static final class RendererSupportImpl implements RendererSupport {
617         /**
618          * Renderer map.
619          */
620        private final RendererMap renderers = new RendererMap();
621 
622         /**
623          * Create new instance.
624          */
625        public RendererSupportImpl() {
626            super();
627        }
628 
629         /** {@inheritDoc} */
630        public RendererMap getRendererMap() {
631            return renderers;
632        }
633 
634         /** {@inheritDoc} */
635        public void setRenderer(final Class renderedClass,
636                                final ObjectRenderer renderer) {
637            renderers.put(renderedClass, renderer);
638        }
639    }
640 
641     /**
642      * Proxy that implements HierarchyEventListener
643      * and delegates to LoggerEventListener.
644      */
645    private static final class HierarchyEventListenerProxy
646         implements HierarchyEventListener {
647         /**
648          * Wrapper listener.
649          */
650        private LoggerEventListener listener;
651 
652         /**
653          * Creates new instance.
654          * @param l listener
655          */
656        public HierarchyEventListenerProxy(final LoggerEventListener l) {
657            super();
658            if (l == null) {
659                throw new NullPointerException("l");
660            }
661            listener = l;
662        }
663 
664         /** {@inheritDoc} */
665        public void addAppenderEvent(final Category cat,
666                                     final Appender appender) {
667            if (isEnabled() && cat instanceof Logger) {
668                 listener.appenderAddedEvent((Logger) cat, appender);
669            }
670        }
671 
672         /** {@inheritDoc} */
673        public void removeAppenderEvent(final Category cat,
674                                     final Appender appender) {
675            if (isEnabled() && cat instanceof Logger) {
676                 listener.appenderRemovedEvent((Logger) cat, appender);
677            }
678        }
679 
680         /**
681          * Disable forwarding of notifications to
682          * simulate removal of listener.
683          */
684        public synchronized void disable() {
685            listener = null;
686        }
687 
688         /**
689          * Gets whether proxy is enabled.
690          * @return true if proxy is enabled.
691          */
692        private synchronized boolean isEnabled() {
693            return listener != null;
694        }
695    }
696 
697 }