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 Paul Hammant                                             *
009 *****************************************************************************/
010
011package org.picocontainer.gems.monitors;
012
013import static org.picocontainer.monitors.ComponentMonitorHelper.ctorToString;
014import static org.picocontainer.monitors.ComponentMonitorHelper.format;
015import static org.picocontainer.monitors.ComponentMonitorHelper.memberToString;
016import static org.picocontainer.monitors.ComponentMonitorHelper.methodToString;
017import static org.picocontainer.monitors.ComponentMonitorHelper.parmsToString;
018
019import java.io.IOException;
020import java.io.ObjectInputStream;
021import java.io.ObjectOutputStream;
022import java.io.Serializable;
023import java.lang.reflect.Constructor;
024import java.lang.reflect.Member;
025import java.lang.reflect.Method;
026
027import org.apache.log4j.LogManager;
028import org.apache.log4j.Logger;
029import org.apache.log4j.Priority;
030import org.picocontainer.ComponentAdapter;
031import org.picocontainer.ComponentMonitor;
032import org.picocontainer.MutablePicoContainer;
033import org.picocontainer.PicoContainer;
034import org.picocontainer.Injector;
035import org.picocontainer.Behavior;
036import org.picocontainer.monitors.ComponentMonitorHelper;
037import org.picocontainer.monitors.NullComponentMonitor;
038
039
040/**
041 * A {@link org.picocontainer.ComponentMonitor} which writes to a Log4J {@link org.apache.log4j.Logger} instance.
042 * The Logger instance can either be injected or, if not set, the {@link LogManager LogManager}
043 * will be used to retrieve it at every invocation of the monitor.
044 *
045 * @author Paul Hammant
046 * @author Mauro Talevi
047 */
048@SuppressWarnings("serial")
049public class Log4JComponentMonitor implements ComponentMonitor, Serializable {
050
051
052        /**
053         * Log4j Logger.
054         */
055    private transient Logger logger;
056    
057    /**
058     * Delegate Monitor.
059     */
060    private final ComponentMonitor delegate;
061
062    /**
063     * Creates a Log4JComponentMonitor with no Logger instance set.
064     * The {@link LogManager LogManager} will be used to retrieve the Logger instance
065     * at every invocation of the monitor.
066     */
067    public Log4JComponentMonitor() {
068        delegate = new NullComponentMonitor();
069        
070    }
071    
072    /**
073     * Creates a Log4JComponentMonitor with a given Logger instance class.
074     * The class name is used to retrieve the Logger instance.
075     *
076     * @param loggerClass the class of the Logger
077     */
078    public Log4JComponentMonitor(final Class<?> loggerClass) {
079        this(loggerClass.getName());
080    }
081
082    /**
083     * Creates a Log4JComponentMonitor with a given Logger instance name. It uses the
084     * {@link org.apache.log4j.LogManager LogManager} to create the Logger instance.
085     *
086     * @param loggerName the name of the Log
087     */
088    public Log4JComponentMonitor(final String loggerName) {
089        this(LogManager.getLogger(loggerName));
090    }
091
092    /**
093     * Creates a Log4JComponentMonitor with a given Logger instance
094     *
095     * @param logger the Logger to write to
096     */
097    public Log4JComponentMonitor(final Logger logger) {
098        this();
099        this.logger = logger;
100    }
101
102    /**
103     * Creates a Log4JComponentMonitor with a given Logger instance class.
104     * The class name is used to retrieve the Logger instance.
105     *
106     * @param loggerClass the class of the Logger
107     * @param delegate the delegate
108     */
109    public Log4JComponentMonitor(final Class<?> loggerClass, final ComponentMonitor delegate) {
110        this(loggerClass.getName(), delegate);
111    }
112
113    /**
114     * Creates a Log4JComponentMonitor with a given Logger instance name. It uses the
115     * {@link org.apache.log4j.LogManager LogManager} to create the Logger instance.
116     *
117     * @param loggerName the name of the Log
118     * @param delegate the delegate
119     */
120    public Log4JComponentMonitor(final String loggerName, final ComponentMonitor delegate) {
121        this(LogManager.getLogger(loggerName), delegate);
122    }
123
124    /**
125     * Creates a Log4JComponentMonitor with a given Logger instance
126     *
127     * @param logger the Logger to write to
128     * @param delegate the delegate
129     */
130    public Log4JComponentMonitor(final Logger logger, final ComponentMonitor delegate) {
131        this(delegate);
132        this.logger = logger;
133    }
134
135    public Log4JComponentMonitor(final ComponentMonitor delegate) {
136        this.delegate = delegate;
137    }
138
139    /** {@inheritDoc} **/
140    public <T> Constructor<T> instantiating(final PicoContainer container, final ComponentAdapter<T> componentAdapter,
141                                     final Constructor<T> constructor
142    ) {
143        Logger logger = getLogger(constructor);
144        if (logger.isDebugEnabled()) {
145            logger.debug(format(ComponentMonitorHelper.INSTANTIATING, ctorToString(constructor)));
146        }
147        return delegate.instantiating(container, componentAdapter, constructor);
148    }
149
150    /** {@inheritDoc} **/
151    public <T> void instantiated(final PicoContainer container, final ComponentAdapter<T> componentAdapter,
152                             final Constructor<T> constructor,
153                             final Object instantiated,
154                             final Object[] parameters,
155                             final long duration) {
156        Logger logger = getLogger(constructor);
157        if (logger.isDebugEnabled()) {
158            logger.debug(format(ComponentMonitorHelper.INSTANTIATED, ctorToString(constructor), duration, instantiated.getClass().getName(), parmsToString(parameters)));
159        }
160        delegate.instantiated(container, componentAdapter, constructor, instantiated, parameters, duration);
161    }
162
163    /** {@inheritDoc} **/
164    public <T> void instantiationFailed(final PicoContainer container,
165                                    final ComponentAdapter<T> componentAdapter,
166                                    final Constructor<T> constructor,
167                                    final Exception cause) {
168        Logger logger = getLogger(constructor);
169        if (logger.isEnabledFor(Priority.WARN)) {
170            logger.warn(format(ComponentMonitorHelper.INSTANTIATION_FAILED, ctorToString(constructor), cause.getMessage()), cause);
171        }
172        delegate.instantiationFailed(container, componentAdapter, constructor, cause);
173    }
174
175    /** {@inheritDoc} **/
176    public Object invoking(final PicoContainer container,
177                         final ComponentAdapter<?> componentAdapter,
178                         final Member member,
179                         final Object instance, Object[] args) {
180        Logger logger = getLogger(member);
181        if (logger.isDebugEnabled()) {
182            logger.debug(format(ComponentMonitorHelper.INVOKING, memberToString(member), instance));
183        }
184        return delegate.invoking(container, componentAdapter, member, instance, args);
185    }
186
187    /** {@inheritDoc} **/
188    public void invoked(PicoContainer container,
189                        ComponentAdapter<?> componentAdapter,
190                        Member member,
191                        Object instance,
192                        long duration,
193                        Object[] args, Object retVal) {
194        Logger logger = getLogger(member);
195        if (logger.isDebugEnabled()) {
196            logger.debug(format(ComponentMonitorHelper.INVOKED, methodToString(member), instance, duration));
197        }
198        delegate.invoked(container, componentAdapter, member, instance, duration, args, retVal);
199    }
200
201    /** {@inheritDoc} **/
202    public void invocationFailed(final Member member, final Object instance, final Exception cause) {
203        Logger logger = getLogger(member);
204        if (logger.isEnabledFor(Priority.WARN)) {
205            logger.warn(format(ComponentMonitorHelper.INVOCATION_FAILED, memberToString(member), instance, cause.getMessage()), cause);
206        }
207        delegate.invocationFailed(member, instance, cause);
208    }
209
210    /** {@inheritDoc} **/
211    public void lifecycleInvocationFailed(final MutablePicoContainer container,
212                                          final ComponentAdapter<?> componentAdapter, final Method method,
213                                          final Object instance,
214                                          final RuntimeException cause) {
215        Logger logger = getLogger(method);
216        if (logger.isEnabledFor(Priority.WARN)) {
217            logger.warn(format(ComponentMonitorHelper.LIFECYCLE_INVOCATION_FAILED, methodToString(method), instance, cause.getMessage()), cause);
218        }
219        delegate.lifecycleInvocationFailed(container, componentAdapter, method, instance, cause);
220    }
221
222    /** {@inheritDoc} **/
223    public Object noComponentFound(final MutablePicoContainer container, final Object componentKey) {
224        Logger logger = this.logger != null ? this.logger : LogManager.getLogger(ComponentMonitor.class);
225        if (logger.isEnabledFor(Priority.WARN)) {
226            logger.warn(format(ComponentMonitorHelper.NO_COMPONENT, componentKey));
227        }
228        return delegate.noComponentFound(container, componentKey);
229
230    }
231
232    /** {@inheritDoc} */
233    public Injector newInjector(final Injector injector) {
234        return delegate.newInjector(injector);
235    }
236
237    /** {@inheritDoc} **/
238    public Behavior newBehavior(Behavior behavior) {
239        return delegate.newBehavior(behavior);
240    }
241
242    protected synchronized Logger getLogger(final Member member) {
243        if ( logger != null ){
244            return logger;
245        } 
246        return LogManager.getLogger(member.getDeclaringClass());
247    }
248
249    
250    /**
251     * Serializes the monitor.
252     * @param oos object output stream.
253     * @throws IOException
254     */
255    private void writeObject(final ObjectOutputStream oos) throws IOException {
256        oos.defaultWriteObject();
257        if (logger != null) {
258                oos.writeBoolean(true);
259                oos.writeUTF(logger.getName());
260        } else {
261                oos.writeBoolean(false);                        
262        }
263    }
264    
265    /**
266     * Manually creates a new logger instance if it was defined earlier.
267     * @param ois
268     * @throws IOException
269     * @throws ClassNotFoundException
270     */
271    private void readObject(final ObjectInputStream ois) throws IOException, ClassNotFoundException {
272        ois.defaultReadObject();
273        boolean hasDefaultLogger = ois.readBoolean();
274        if (hasDefaultLogger) {
275                String defaultLoggerCategory = ois.readUTF();
276                assert defaultLoggerCategory != null : "Serialization indicated default logger, "
277                        +"but no logger category found in input stream.";
278                logger = LogManager.getLogger(defaultLoggerCategory);
279        }
280    }
281
282}