001    /*****************************************************************************
002     * Copyright (C) PicoContainer Organization. All rights reserved.            *
003     * ------------------------------------------------------------------------- *
004     * The software in this package is published under the terms of the BSD      *
005     * style license a copy of which has been included with this distribution in *
006     * the LICENSE.txt file.                                                     *
007     *                                                                           *
008     * Original Code By Centerline Computers, Inc.                               *
009     *****************************************************************************/
010    
011    package org.picocontainer.gems.containers;
012    
013    import java.io.ObjectInputStream;
014    import java.io.ObjectOutputStream;
015    import java.io.Serializable;
016    import java.util.Collection;
017    import java.util.List;
018    
019    import org.apache.log4j.Logger;
020    import org.picocontainer.ComponentAdapter;
021    import org.picocontainer.MutablePicoContainer;
022    import org.picocontainer.Parameter;
023    import org.picocontainer.PicoContainer;
024    import org.picocontainer.PicoVerificationException;
025    import org.picocontainer.PicoVisitor;
026    
027    /**
028     * Decorates a MutablePicoContainer to provide extensive tracing capabilities
029     * for all function calls into the Picocontainers.
030     * <p>
031     * By default, this class uses <tt>org.picocontainer.PicoContainer</tt> as its
032     * logging category, however, this may be changed by providing the logger in its
033     * alternate constructor.
034     * </p>
035     * <p>
036     * Start and Stop events are logged under <tt>info</tt> priority, as are all
037     * conditions where querying for an object returns a null object (e.g.,
038     * getComponentAdapter(Object) returns null). All other functions use
039     * <tt>debug</tt> priority.
040     * </p>
041     * <p>
042     * If used in nanocontainer, you can add wrap your PicoContainer with the
043     * Log4jTracingContainerDecorator: (Groovy Example)
044     * </p>
045     * 
046     * <pre>
047     *              pico = builder.container(parent: parent) {
048     *                      //component(.....)
049     *                      //And others.
050     *              }
051     * 
052     *              //Wrap the underlying NanoContainer with a Decorated Pico.
053     *              pico = new org.picocontainer.gems.containers.Log4jTracingContainerDecorator (pico.getPico())
054     * </pre>
055     * 
056     * @author Michael Rimov
057     * @since Version 1.3
058     */
059    public class Log4jTracingContainerDecorator implements MutablePicoContainer, Serializable {
060    
061            /**
062             * Wrapped container.
063             */
064            private final MutablePicoContainer delegate;
065    
066            /**
067             * Logger instance used for writing events.
068             */
069            private transient Logger logger;
070    
071            /**
072             * Default typical wrapper that wraps another MutablePicoContainer.
073             * 
074             * @param delegate
075             *            Container to be decorated.
076             * @throws NullPointerException
077             *             if delegate is null.
078             */
079            public Log4jTracingContainerDecorator(final MutablePicoContainer delegate) {
080                    this(delegate, Logger.getLogger(PicoContainer.class));
081            }
082    
083            /**
084             * Alternate constructor that allows specification of the Logger to use.
085             * 
086             * @param delegate
087             *            Container to be decorated.
088             * @param logger
089             *            specific Log4j Logger to use.
090             * @throws NullPointerException
091             *             if delegate or logger is null.
092             */
093            public Log4jTracingContainerDecorator(final MutablePicoContainer delegate, final Logger logger) {
094                    if (delegate == null) {
095                            throw new NullPointerException("delegate");
096                    }
097    
098                    if (logger == null) {
099                            throw new NullPointerException("logger");
100                    }
101    
102                    this.delegate = delegate;
103                    this.logger = logger;
104            }
105    
106            /**
107             * Standard message handling for cases when a null object is returned for a
108             * given key.
109             * 
110             * @param componentKey
111             * @param target
112             */
113            protected void onKeyDoesntExistInContainer(final Object componentKey, final Logger target) {
114                    logger.info("Could not find component " + ((componentKey != null) ? componentKey.toString() : " null ")
115                                    + " in container or parent container.");
116            }
117    
118            /**
119             * {@inheritDoc}
120             * 
121             * @param visitor
122             * @see org.picocontainer.PicoContainer#accept(org.picocontainer.PicoVisitor)
123             */
124            public void accept(final PicoVisitor visitor) {
125                    if (logger.isDebugEnabled()) {
126                            logger.debug("Visiting Container " + delegate + " with visitor " + visitor);
127                    }
128                    delegate.accept(visitor);
129            }
130    
131            /**
132             * {@inheritDoc}
133             * 
134             * @param child
135             * @return
136             * @see org.picocontainer.MutablePicoContainer#addChildContainer(org.picocontainer.PicoContainer)
137             */
138            public boolean addChildContainer(final PicoContainer child) {
139                    if (logger.isDebugEnabled()) {
140                            logger.debug("Adding child container: " + child + " to container " + delegate);
141                    }
142                    return delegate.addChildContainer(child);
143            }
144    
145            /**
146             * {@inheritDoc}
147             * 
148             * @see org.picocontainer.Disposable#dispose()
149             */
150            public void dispose() {
151                    if (logger.isDebugEnabled()) {
152                            logger.debug("Disposing container " + delegate);
153                    }
154                    delegate.dispose();
155            }
156    
157            /**
158             * {@inheritDoc}
159             * 
160             * @param componentKey
161             * @return
162             * @see org.picocontainer.PicoContainer#getComponentAdapter(java.lang.Object)
163             */
164            public ComponentAdapter getComponentAdapter(final Object componentKey) {
165                    if (logger.isDebugEnabled()) {
166                            logger.debug("Locating component adapter with key " + componentKey);
167                    }
168    
169                    ComponentAdapter adapter = delegate.getComponentAdapter(componentKey);
170                    if (adapter == null) {
171                            onKeyDoesntExistInContainer(componentKey, logger);
172                    }
173                    return adapter;
174            }
175    
176            /**
177             * {@inheritDoc}
178             * 
179             * @param componentType
180             * @return ComponentAdapter or null.
181             * @see org.picocontainer.PicoContainer#getComponentAdapterOfType(java.lang.Class)
182             */
183            public ComponentAdapter getComponentAdapterOfType(final Class componentType) {
184                    if (logger.isDebugEnabled()) {
185                            logger.debug("Locating component adapter with type " + componentType);
186                    }
187    
188                    ComponentAdapter ca = delegate.getComponentAdapterOfType(componentType);
189    
190                    if (ca == null) {
191                            onKeyDoesntExistInContainer(ca, logger);
192                    }
193                    return ca;
194            }
195    
196            /**
197             * {@inheritDoc}
198             * 
199             * @return Collection or null.
200             * @see org.picocontainer.PicoContainer#getComponentAdapters()
201             */
202            public Collection getComponentAdapters() {
203                    if (logger.isDebugEnabled()) {
204                            logger.debug("Grabbing all component adapters for container: " + delegate);
205                    }
206                    return delegate.getComponentAdapters();
207            }
208    
209            /**
210             * {@inheritDoc}
211             * 
212             * @param componentType
213             * @return List of ComponentAdapters
214             * @see org.picocontainer.PicoContainer#getComponentAdaptersOfType(java.lang.Class)
215             */
216            public List getComponentAdaptersOfType(final Class componentType) {
217                    if (logger.isDebugEnabled()) {
218                            logger.debug("Grabbing all component adapters for container: " + delegate + " of type: "
219                                            + componentType.getName());
220                    }
221                    return delegate.getComponentAdaptersOfType(componentType);
222            }
223    
224            /**
225             * {@inheritDoc}
226             * 
227             * @param componentKey
228             * @return
229             * @see org.picocontainer.PicoContainer#getComponentInstance(java.lang.Object)
230             */
231            public Object getComponentInstance(final Object componentKey) {
232    
233                    if (logger.isDebugEnabled()) {
234                            logger.debug("Attempting to load component instance with key: " + componentKey + " for container "
235                                            + delegate);
236    
237                    }
238    
239                    Object result = delegate.getComponentInstance(componentKey);
240                    if (result == null) {
241                            onKeyDoesntExistInContainer(componentKey, logger);
242                    }
243    
244                    return result;
245            }
246    
247            /**
248             * {@inheritDoc}
249             * 
250             * @param componentType
251             * @return
252             * @see org.picocontainer.PicoContainer#getComponentInstanceOfType(java.lang.Class)
253             */
254            public Object getComponentInstanceOfType(final Class componentType) {
255                    if (logger.isDebugEnabled()) {
256                            logger.debug("Attempting to load component instance with type: " + componentType + " for container "
257                                            + delegate);
258    
259                    }
260    
261                    Object result = delegate.getComponentInstanceOfType(componentType);
262                    if (result == null) {
263                            if (logger.isInfoEnabled()) {
264                                    logger.info("No component of type " + componentType.getName() + " was found in container: " + delegate);
265                            }
266                    }
267    
268                    return result;
269            }
270    
271            /**
272             * {@inheritDoc}
273             * 
274             * @return
275             * @see org.picocontainer.PicoContainer#getComponentInstances()
276             */
277            public List getComponentInstances() {
278                    if (logger.isDebugEnabled()) {
279                            logger.debug("Retrieving all component instances for container " + delegate);
280                    }
281                    return delegate.getComponentInstances();
282            }
283    
284            /**
285             * {@inheritDoc}
286             * 
287             * @param componentType
288             * @return
289             * @see org.picocontainer.PicoContainer#getComponentInstancesOfType(java.lang.Class)
290             */
291            public List getComponentInstancesOfType(final Class componentType) {
292                    if (logger.isDebugEnabled()) {
293                            logger.debug("Loading all component instances of type " + componentType + " for container " + delegate);
294                    }
295                    List result = delegate.getComponentInstancesOfType(componentType);
296                    if (result == null || result.size() == 0) {
297                            if (logger.isInfoEnabled()) {
298                                    logger.info("Could not find any components  " + " in container or parent container.");
299                            }
300                    }
301    
302                    return result;
303            }
304    
305            /**
306             * {@inheritDoc}
307             * 
308             * @return
309             * @see org.picocontainer.PicoContainer#getParent()
310             */
311            public PicoContainer getParent() {
312                    if (logger.isDebugEnabled()) {
313                            logger.debug("Retrieving the parent for container " + delegate);
314                    }
315    
316                    return delegate.getParent();
317            }
318    
319            /**
320             * {@inheritDoc}
321             * 
322             * @return
323             * @see org.picocontainer.MutablePicoContainer#makeChildContainer()
324             */
325            public MutablePicoContainer makeChildContainer() {
326                    if (logger.isDebugEnabled()) {
327                            logger.debug("Making child container for container " + delegate);
328                    }
329    
330                    // Wrap the new delegate
331                    return new Log4jTracingContainerDecorator(delegate.makeChildContainer());
332            }
333    
334            /**
335             * {@inheritDoc}
336             * 
337             * @param componentAdapter
338             * @return
339             * @see org.picocontainer.MutablePicoContainer#registerComponent(org.picocontainer.ComponentAdapter)
340             */
341            public ComponentAdapter registerComponent(final ComponentAdapter componentAdapter) {
342                    if (logger.isDebugEnabled()) {
343                            logger.debug("Registering component adapter " + componentAdapter);
344                    }
345    
346                    return delegate.registerComponent(componentAdapter);
347            }
348    
349            /**
350             * {@inheritDoc}
351             * 
352             * @param componentImplementation
353             * @return
354             * @see org.picocontainer.MutablePicoContainer#registerComponentImplementation(java.lang.Class)
355             */
356            public ComponentAdapter registerComponentImplementation(final Class componentImplementation) {
357                    if (logger.isDebugEnabled()) {
358                            logger.debug("Registering component implementation " + componentImplementation.getName());
359                    }
360    
361                    return delegate.registerComponentImplementation(componentImplementation);
362            }
363    
364            /**
365             * {@inheritDoc}
366             * 
367             * @param componentKey
368             * @param componentImplementation
369             * @param parameters
370             * @return
371             * @see org.picocontainer.MutablePicoContainer#registerComponentImplementation(java.lang.Object,
372             *      java.lang.Class, org.picocontainer.Parameter[])
373             */
374            public ComponentAdapter registerComponentImplementation(final Object componentKey,
375                            final Class componentImplementation, final Parameter[] parameters) {
376    
377                    if (logger.isDebugEnabled()) {
378                            logger.debug("Registering component implementation with key " + componentKey + " and implementation "
379                                            + componentImplementation.getName() + " using parameters " + parameters);
380                    }
381    
382                    return delegate.registerComponentImplementation(componentKey, componentImplementation, parameters);
383            }
384    
385            /**
386             * {@inheritDoc}
387             * 
388             * @param componentKey
389             * @param componentImplementation
390             * @return
391             * @see org.picocontainer.MutablePicoContainer#registerComponentImplementation(java.lang.Object,
392             *      java.lang.Class)
393             */
394            public ComponentAdapter registerComponentImplementation(final Object componentKey,
395                            final Class componentImplementation) {
396                    if (logger.isDebugEnabled()) {
397                            logger.debug("Registering component implementation with key " + componentKey + " and implementation "
398                                            + componentImplementation.getName());
399                    }
400    
401                    return delegate.registerComponentImplementation(componentKey, componentImplementation);
402            }
403    
404            /**
405             * {@inheritDoc}
406             * 
407             * @param componentKey
408             * @param componentInstance
409             * @return
410             * @see org.picocontainer.MutablePicoContainer#registerComponentInstance(java.lang.Object,
411             *      java.lang.Object)
412             */
413            public ComponentAdapter registerComponentInstance(final Object componentKey, final Object componentInstance) {
414                    if (logger.isDebugEnabled()) {
415                            logger.debug("Registering component instance with key " + componentKey + " and instance "
416                                            + componentInstance + "(class: "
417                                            + ((componentInstance != null) ? componentInstance.getClass().getName() : " null "));
418                    }
419    
420                    return delegate.registerComponentInstance(componentKey, componentInstance);
421            }
422    
423            /**
424             * {@inheritDoc}
425             * 
426             * @param componentInstance
427             * @return
428             * @see org.picocontainer.MutablePicoContainer#registerComponentInstance(java.lang.Object)
429             */
430            public ComponentAdapter registerComponentInstance(final Object componentInstance) {
431                    if (logger.isDebugEnabled()) {
432                            logger.debug("Registering component instance " + componentInstance + "(class: "
433                                            + ((componentInstance != null) ? componentInstance.getClass().getName() : " null "));
434                    }
435    
436                    return delegate.registerComponentInstance(componentInstance);
437            }
438    
439            /**
440             * {@inheritDoc}
441             * 
442             * @param child
443             * @return
444             * @see org.picocontainer.MutablePicoContainer#removeChildContainer(org.picocontainer.PicoContainer)
445             */
446            public boolean removeChildContainer(final PicoContainer child) {
447                    if (logger.isDebugEnabled()) {
448                            logger.debug("Removing child container: " + child + " from parent: " + delegate);
449                    }
450                    return delegate.removeChildContainer(child);
451            }
452    
453            /**
454             * {@inheritDoc}
455             * 
456             * @see org.picocontainer.Startable#start()
457             */
458            public void start() {
459                    if (logger.isInfoEnabled()) {
460                            logger.info("Starting Container " + delegate);
461                    }
462    
463                    delegate.start();
464            }
465    
466            /**
467             * {@inheritDoc}
468             * 
469             * @see org.picocontainer.Startable#stop()
470             */
471            public void stop() {
472                    if (logger.isInfoEnabled()) {
473                            logger.info("Stopping Container " + delegate);
474                    }
475                    delegate.stop();
476            }
477    
478            /**
479             * {@inheritDoc}
480             * 
481             * @param componentKey
482             * @return
483             * @see org.picocontainer.MutablePicoContainer#unregisterComponent(java.lang.Object)
484             */
485            public ComponentAdapter unregisterComponent(final Object componentKey) {
486                    if (logger.isDebugEnabled()) {
487                            logger.debug("Unregistering component " + componentKey + " from container " + delegate);
488                    }
489    
490                    return delegate.unregisterComponent(componentKey);
491            }
492    
493            /**
494             * {@inheritDoc}
495             * 
496             * @param componentInstance
497             * @return
498             * @see org.picocontainer.MutablePicoContainer#unregisterComponentByInstance(java.lang.Object)
499             */
500            public ComponentAdapter unregisterComponentByInstance(final Object componentInstance) {
501                    if (logger.isDebugEnabled()) {
502                            logger.debug("Unregistering component by instance (" + componentInstance + ") from container " + delegate);
503                    }
504    
505                    return delegate.unregisterComponentByInstance(componentInstance);
506            }
507    
508            /**
509             * {@inheritDoc}
510             * 
511             * @throws PicoVerificationException
512             * @deprecated
513             * @see org.picocontainer.PicoContainer#verify()
514             */
515            public void verify() throws PicoVerificationException {
516                    logger.info("Verifying container");
517                    logger.warn("Using deprecated function PicoContainer.verify().  "
518                                    + "Please use VerifyingVisitor instead.");
519                    delegate.verify();
520            }
521    
522            /**
523             * Retrieves the logger instance used by this decorator.
524             * 
525             * @return Logger instance.
526             */
527            public Logger getLoggerUsed() {
528                    return this.logger;
529            }
530    
531            private void readObject(final ObjectInputStream s) throws java.io.IOException, java.lang.ClassNotFoundException {
532    
533                    s.defaultReadObject();
534                    String loggerName = s.readUTF();
535                    logger = Logger.getLogger(loggerName);
536            }
537    
538            private void writeObject(final ObjectOutputStream s) throws java.io.IOException {
539                    s.defaultWriteObject();
540                    s.writeUTF(logger.getName());
541            }
542    
543    }