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 *****************************************************************************/
010package org.picocontainer.gems.monitors;
011
012import static org.picocontainer.monitors.ComponentMonitorHelper.ctorToString;
013import static org.picocontainer.monitors.ComponentMonitorHelper.format;
014import static org.picocontainer.monitors.ComponentMonitorHelper.memberToString;
015import static org.picocontainer.monitors.ComponentMonitorHelper.methodToString;
016import static org.picocontainer.monitors.ComponentMonitorHelper.parmsToString;
017
018import java.io.IOException;
019import java.io.ObjectInputStream;
020import java.io.ObjectOutputStream;
021import java.io.Serializable;
022import java.lang.reflect.Constructor;
023import java.lang.reflect.Member;
024import java.lang.reflect.Method;
025
026import org.picocontainer.ComponentAdapter;
027import org.picocontainer.ComponentMonitor;
028import org.picocontainer.MutablePicoContainer;
029import org.picocontainer.PicoContainer;
030import org.picocontainer.Injector;
031import org.picocontainer.Behavior;
032import org.picocontainer.monitors.ComponentMonitorHelper;
033import org.picocontainer.monitors.NullComponentMonitor;
034import org.slf4j.Logger;
035import 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")
048public 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 synchronized 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}