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 }