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