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 Mauro Talevi                                             *
009     *****************************************************************************/
010    
011    package org.picocontainer.gems.monitors;
012    
013    import static org.picocontainer.monitors.ComponentMonitorHelper.ctorToString;
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.Serializable;
019    import java.lang.reflect.Constructor;
020    import java.lang.reflect.Member;
021    import java.lang.reflect.Method;
022    
023    import org.apache.commons.logging.Log;
024    import org.apache.commons.logging.LogFactory;
025    import org.picocontainer.ComponentAdapter;
026    import org.picocontainer.ComponentMonitor;
027    import org.picocontainer.MutablePicoContainer;
028    import org.picocontainer.PicoContainer;
029    import org.picocontainer.injectors.AbstractInjector;
030    import org.picocontainer.monitors.ComponentMonitorHelper;
031    import org.picocontainer.monitors.NullComponentMonitor;
032    
033    
034    /**
035     * A {@link ComponentMonitor} which writes to a Commons Logging {@link Log Log} instance.
036     * The Log instance can either be injected or, if not set, the {@link LogFactory LogFactory}
037     * will be used to retrieve it at every invocation of the monitor.
038     * <h4>Note on Serialization</h4>
039     * <p>Commons Logging does <em>not</em> guarantee Serialization.  It is supported when using Log4j
040     * as a back end, but you should write a test case to determine if your particular logger implementation
041     * is supported if you plan on serializing this ComponentMonitor.</p>
042     * 
043     * @author Paul Hammant
044     * @author Mauro Talevi
045     */
046    @SuppressWarnings("serial")
047    public class CommonsLoggingComponentMonitor implements ComponentMonitor, Serializable {
048    
049    
050            /**
051             * Commons Logger.
052             */
053            private Log log;
054            
055       
056            /**
057             * Delegate for component monitor chains.
058             */
059        private final ComponentMonitor delegate;
060    
061        /**
062         * Creates a CommonsLoggingComponentMonitor with no Log instance set.
063         * The {@link LogFactory LogFactory} will be used to retrieve the Log instance
064         * at every invocation of the monitor.
065         */
066        public CommonsLoggingComponentMonitor() {
067            delegate = new NullComponentMonitor();
068        }
069    
070        /**
071         * Creates a CommonsLoggingComponentMonitor with a given Log instance class.
072         * The class name is used to retrieve the Log instance.
073         * 
074         * @param logClass the class of the Log
075         */
076        public CommonsLoggingComponentMonitor(final Class<?> logClass) {
077            this(logClass.getName());
078        }
079    
080        /**
081         * Creates a CommonsLoggingComponentMonitor with a given Log instance name. It uses the
082         * {@link LogFactory LogFactory} to create the Log instance.
083         * 
084         * @param logName the name of the Log
085         */
086        public CommonsLoggingComponentMonitor(final String logName) {
087            this(LogFactory.getLog(logName));
088        }
089    
090        /**
091         * Creates a CommonsLoggingComponentMonitor with a given Log instance
092         * @param log the Log to write to
093         */
094        public CommonsLoggingComponentMonitor(final Log log) {
095            this();
096            this.log = log;        
097        }
098    
099        /**
100         * Creates a CommonsLoggingComponentMonitor with a given Log instance class.
101         * The class name is used to retrieve the Log instance.
102         *
103         * @param logClass the class of the Log
104         * @param delegate the delegate
105         */
106        public CommonsLoggingComponentMonitor(final Class<?> logClass, final ComponentMonitor delegate) {
107            this(logClass.getName(), delegate);
108        }
109    
110        /**
111         * Creates a CommonsLoggingComponentMonitor with a given Log instance name. It uses the
112         * {@link LogFactory LogFactory} to create the Log instance.
113         *
114         * @param logName the name of the Log
115         * @param delegate the delegate
116         */
117        public CommonsLoggingComponentMonitor(final String logName, final ComponentMonitor delegate) {
118            this(LogFactory.getLog(logName), delegate);
119        }
120    
121        /**
122         * Creates a CommonsLoggingComponentMonitor with a given Log instance.
123         * @param log the Log with which to write events.
124         * @param delegate the delegate
125         */
126        public CommonsLoggingComponentMonitor(final Log log, final ComponentMonitor delegate) {
127            this.log = log;
128            this.delegate = delegate;
129        }
130    
131    
132        /** {@inheritDoc} **/
133       public <T> Constructor<T> instantiating(final PicoContainer container, final ComponentAdapter<T> componentAdapter,
134                                         final Constructor<T> constructor
135        ) {
136            Log log = getLog(constructor);
137            if (log.isDebugEnabled()) {
138                log.debug(ComponentMonitorHelper.format(ComponentMonitorHelper.INSTANTIATING, ctorToString(constructor)));
139            }
140            return delegate.instantiating(container, componentAdapter, constructor);
141        }
142    
143       /** {@inheritDoc} **/
144        public <T> void instantiated(final PicoContainer container, final ComponentAdapter<T> componentAdapter,
145                                 final Constructor<T> constructor,
146                                 final Object instantiated,
147                                 final Object[] parameters,
148                                 final long duration) {
149            Log log = getLog(constructor);
150            if (log.isDebugEnabled()) {
151                log.debug(ComponentMonitorHelper.format(ComponentMonitorHelper.INSTANTIATED, ctorToString(constructor), duration, instantiated.getClass().getName(), parmsToString(parameters)));
152            }
153            delegate.instantiated(container, componentAdapter, constructor, instantiated, parameters, duration);
154        }
155    
156        /** {@inheritDoc} **/
157        public <T> void instantiationFailed(final PicoContainer container,
158                                        final ComponentAdapter<T>  componentAdapter,
159                                        final Constructor<T>  constructor,
160                                        final Exception cause) {
161            Log log = getLog(constructor);
162            if (log.isWarnEnabled()) {
163                log.warn(ComponentMonitorHelper.format(ComponentMonitorHelper.INSTANTIATION_FAILED, ctorToString(constructor), cause.getMessage()), cause);
164            }
165            delegate.instantiationFailed(container, componentAdapter, constructor, cause);
166        }
167    
168        /** {@inheritDoc} **/
169        public void invoking(final PicoContainer container,
170                             final ComponentAdapter<?> componentAdapter,
171                             final Member member,
172                             final Object instance) {
173            Log log = getLog(member);
174            if (log.isDebugEnabled()) {
175                log.debug(ComponentMonitorHelper.format(ComponentMonitorHelper.INVOKING, memberToString(member), instance));
176            }
177            delegate.invoking(container, componentAdapter, member, instance);
178        }
179    
180        /** {@inheritDoc} **/
181        public void invoked(final PicoContainer container,
182                            final ComponentAdapter<?> componentAdapter,
183                            final Method method,
184                            final Object instance,
185                            final long duration) {
186            Log log = getLog(method);
187            if (log.isDebugEnabled()) {
188                log.debug(ComponentMonitorHelper.format(ComponentMonitorHelper.INVOKED, methodToString(method), instance, duration));
189            }
190            delegate.invoked(container, componentAdapter, method, instance,  duration);
191        }
192    
193        /** {@inheritDoc} **/
194        public void invocationFailed(final Member member, final Object instance, final Exception cause) {
195            Log log = getLog(member);
196            if (log.isWarnEnabled()) {
197                log.warn(ComponentMonitorHelper.format(ComponentMonitorHelper.INVOCATION_FAILED, memberToString(member), instance, cause.getMessage()), cause);
198            }
199            delegate.invocationFailed(member, instance, cause);
200        }
201    
202        /** {@inheritDoc} **/
203        public void lifecycleInvocationFailed(final MutablePicoContainer container,
204                                              final ComponentAdapter<?> componentAdapter, final Method method,
205                                              final Object instance,
206                                              final RuntimeException cause) {
207            Log log = getLog(method);
208            if (log.isWarnEnabled()) {
209                log.warn(ComponentMonitorHelper.format(ComponentMonitorHelper.LIFECYCLE_INVOCATION_FAILED, methodToString(method), instance, cause.getMessage()), cause);
210            }
211            delegate.lifecycleInvocationFailed(container, componentAdapter, method, instance, cause);
212        }
213    
214        /** {@inheritDoc} **/
215        public Object noComponentFound(final MutablePicoContainer container, final Object componentKey) {
216            Log log = this.log != null ? this.log : LogFactory.getLog(ComponentMonitor.class);
217            if (log.isWarnEnabled()) {
218                log.warn(ComponentMonitorHelper.format(ComponentMonitorHelper.NO_COMPONENT, componentKey));
219            }
220            return delegate.noComponentFound(container, componentKey);
221        }
222    
223        /** {@inheritDoc} **/
224        public AbstractInjector newInjectionFactory(final AbstractInjector abstractInjector) {
225            return delegate.newInjectionFactory(abstractInjector); 
226        }
227    
228        /**
229         * Retrieves the logger appropriate for the calling member's class.
230         * @param member constructor/method/field who's callback is required.
231         * @return the Commons logging instance.
232         */
233        protected Log getLog(final Member member) {
234            if ( log != null ){
235                return log;
236            } 
237            return LogFactory.getLog(member.getDeclaringClass());
238        }
239    
240        
241    }