001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.aop.interceptor;
018:
019: import java.io.Serializable;
020:
021: import org.aopalliance.intercept.MethodInterceptor;
022: import org.aopalliance.intercept.MethodInvocation;
023: import org.apache.commons.logging.Log;
024: import org.apache.commons.logging.LogFactory;
025:
026: import org.springframework.aop.support.AopUtils;
027:
028: /**
029: * Base <code>MethodInterceptor</code> implementation for tracing.
030: *
031: * <p>By default, log messages are written to the log for the interceptor class,
032: * not the class which is being intercepted. Setting the <code>useDynamicLogger</code>
033: * bean property to <code>true</code> causes all log messages to be written to
034: * the <code>Log</code> for the target class being intercepted.
035: *
036: * <p>Subclasses must implement the <code>invokeUnderTrace</code> method, which
037: * is invoked by this class ONLY when a particular invocation SHOULD be traced.
038: * Subclasses should write to the <code>Log</code> instance provided.
039: *
040: * @author Rob Harrop
041: * @author Juergen Hoeller
042: * @since 1.2
043: * @see #setUseDynamicLogger
044: * @see #invokeUnderTrace(org.aopalliance.intercept.MethodInvocation, org.apache.commons.logging.Log)
045: */
046: public abstract class AbstractTraceInterceptor implements
047: MethodInterceptor, Serializable {
048:
049: /**
050: * The default <code>Log</code> instance used to write trace messages.
051: * This instance is mapped to the implementing <code>Class</code>.
052: */
053: protected transient Log defaultLogger = LogFactory
054: .getLog(getClass());
055:
056: /**
057: * Indicates whether or not proxy class names should be hidden when using dynamic loggers.
058: * @see #setUseDynamicLogger
059: */
060: private boolean hideProxyClassNames = false;
061:
062: /**
063: * Set whether to use a dynamic logger or a static logger.
064: * Default is a static logger for this trace interceptor.
065: * <p>Used to determine which <code>Log</code> instance should be used to write
066: * log messages for a particular method invocation: a dynamic one for the
067: * <code>Class</code> getting called, or a static one for the <code>Class</code>
068: * of the trace interceptor.
069: * <p><b>NOTE:</b> Specify either this property or "loggerName", not both.
070: * @see #getLoggerForInvocation(org.aopalliance.intercept.MethodInvocation)
071: */
072: public void setUseDynamicLogger(boolean useDynamicLogger) {
073: // Release default logger if it is not being used.
074: this .defaultLogger = (useDynamicLogger ? null : LogFactory
075: .getLog(getClass()));
076: }
077:
078: /**
079: * Set the name of the logger to use. The name will be passed to the
080: * underlying logger implementation through Commons Logging, getting
081: * interpreted as log category according to the logger's configuration.
082: * <p>This can be specified to not log into the category of a class
083: * (whether this interceptor's class or the class getting called)
084: * but rather into a specific named category.
085: * <p><b>NOTE:</b> Specify either this property or "useDynamicLogger", not both.
086: * @see org.apache.commons.logging.LogFactory#getLog(String)
087: * @see org.apache.log4j.Logger#getLogger(String)
088: * @see java.util.logging.Logger#getLogger(String)
089: */
090: public void setLoggerName(String loggerName) {
091: this .defaultLogger = LogFactory.getLog(loggerName);
092: }
093:
094: /**
095: * Set to "true" to have {@link #setUseDynamicLogger dynamic loggers} hide
096: * proxy class names wherever possible. Default is "false".
097: */
098: public void setHideProxyClassNames(boolean hideProxyClassNames) {
099: this .hideProxyClassNames = hideProxyClassNames;
100: }
101:
102: /**
103: * Determines whether or not logging is enabled for the particular <code>MethodInvocation</code>.
104: * If not, the method invocation proceeds as normal, otherwise the method invocation is passed
105: * to the <code>invokeUnderTrace</code> method for handling.
106: * @see #invokeUnderTrace(org.aopalliance.intercept.MethodInvocation, org.apache.commons.logging.Log)
107: */
108: public Object invoke(MethodInvocation invocation) throws Throwable {
109: Log logger = getLoggerForInvocation(invocation);
110: if (isInterceptorEnabled(invocation, logger)) {
111: return invokeUnderTrace(invocation, logger);
112: } else {
113: return invocation.proceed();
114: }
115: }
116:
117: /**
118: * Return the appropriate <code>Log</code> instance to use for the given
119: * <code>MethodInvocation</code>. If the <code>useDynamicLogger</code> flag
120: * is set, the <code>Log</code> instance will be for the target class of the
121: * <code>MethodInvocation</code>, otherwise the <code>Log</code> will be the
122: * default static logger.
123: * @param invocation the <code>MethodInvocation</code> being traced
124: * @return the <code>Log</code> instance to use
125: * @see #setUseDynamicLogger
126: */
127: protected Log getLoggerForInvocation(MethodInvocation invocation) {
128: if (this .defaultLogger != null) {
129: return this .defaultLogger;
130: } else {
131: Object target = invocation.getThis();
132: Class logCategoryClass = target.getClass();
133: if (this .hideProxyClassNames) {
134: logCategoryClass = AopUtils.getTargetClass(target);
135: }
136: return LogFactory.getLog(logCategoryClass);
137: }
138: }
139:
140: /**
141: * Determine whether the interceptor should kick in, that is,
142: * whether the <code>invokeUnderTrace</code> method should be called.
143: * <p>Default behavior is to check whether the given <code>Log</code>
144: * instance is enabled. Subclasses can override this to apply the
145: * interceptor in other cases as well.
146: * @param invocation the <code>MethodInvocation</code> being traced
147: * @param logger the <code>Log</code> instance to check
148: * @see #invokeUnderTrace
149: * @see #isLogEnabled
150: */
151: protected boolean isInterceptorEnabled(MethodInvocation invocation,
152: Log logger) {
153: return isLogEnabled(logger);
154: }
155:
156: /**
157: * Determine whether the given {@link Log} instance is enabled.
158: * <p>Default is <code>true</code> when the "trace" level is enabled.
159: * Subclasses can override this to change the level under which 'tracing' occurs.
160: * @param logger the <code>Log</code> instance to check
161: */
162: protected boolean isLogEnabled(Log logger) {
163: return logger.isTraceEnabled();
164: }
165:
166: /**
167: * Subclasses must override this method to perform any tracing around the
168: * supplied <code>MethodInvocation</code>. Subclasses are responsible for
169: * ensuring that the <code>MethodInvocation</code> actually executes by
170: * calling <code>MethodInvocation.proceed()</code>.
171: * <p>By default, the passed-in <code>Log</code> instance will have log level
172: * "trace" enabled. Subclasses do not have to check for this again, unless
173: * they overwrite the <code>isInterceptorEnabled</code> method to modify
174: * the default behavior.
175: * @param logger the <code>Log</code> to write trace messages to
176: * @return the result of the call to <code>MethodInvocation.proceed()</code>
177: * @throws Throwable if the call to <code>MethodInvocation.proceed()</code>
178: * encountered any errors
179: * @see #isInterceptorEnabled
180: * @see #isLogEnabled
181: */
182: protected abstract Object invokeUnderTrace(
183: MethodInvocation invocation, Log logger) throws Throwable;
184:
185: }
|