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