001: // Copyright 2006, 2007 The Apache Software Foundation
002: //
003: // Licensed under the Apache License, Version 2.0 (the "License");
004: // you may not use this file except in compliance with the License.
005: // You may obtain a copy of the License at
006: //
007: // http://www.apache.org/licenses/LICENSE-2.0
008: //
009: // Unless required by applicable law or agreed to in writing, software
010: // distributed under the License is distributed on an "AS IS" BASIS,
011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: // See the License for the specific language governing permissions and
013: // limitations under the License.
014:
015: package org.apache.tapestry.ioc.internal.services;
016:
017: import static java.lang.String.format;
018: import static org.apache.tapestry.ioc.services.ClassFabUtils.toJavaClassName;
019:
020: import java.lang.reflect.Constructor;
021: import java.lang.reflect.InvocationTargetException;
022: import java.lang.reflect.Modifier;
023:
024: import org.apache.commons.logging.Log;
025: import org.apache.tapestry.ioc.annotations.InjectService;
026: import org.apache.tapestry.ioc.services.ClassFab;
027: import org.apache.tapestry.ioc.services.ClassFactory;
028: import org.apache.tapestry.ioc.services.ExceptionTracker;
029: import org.apache.tapestry.ioc.services.LoggingDecorator;
030: import org.apache.tapestry.ioc.services.MethodIterator;
031: import org.apache.tapestry.ioc.services.MethodSignature;
032: import org.apache.tapestry.ioc.util.BodyBuilder;
033:
034: public class LoggingDecoratorImpl implements LoggingDecorator {
035: private final ClassFactory _classFactory;
036:
037: private final ExceptionTracker _exceptionTracker;
038:
039: public LoggingDecoratorImpl(@InjectService("ClassFactory")
040: ClassFactory classFactory,
041:
042: ExceptionTracker exceptionTracker) {
043: _classFactory = classFactory;
044: _exceptionTracker = exceptionTracker;
045: }
046:
047: public <T> T build(Class<T> serviceInterface, T delegate,
048: String serviceId, Log serviceLog) {
049: Class interceptorClass = createInterceptorClass(
050: serviceInterface, serviceId);
051:
052: ServiceLogger logger = new ServiceLogger(serviceLog,
053: _exceptionTracker);
054:
055: Constructor cc = interceptorClass.getConstructors()[0];
056:
057: Object interceptor = null;
058: Throwable fail = null;
059:
060: try {
061: interceptor = cc.newInstance(delegate, logger);
062: } catch (InvocationTargetException ite) {
063: fail = ite.getTargetException();
064: } catch (Exception ex) {
065: fail = ex;
066: }
067:
068: if (fail != null)
069: throw new RuntimeException(fail);
070:
071: return serviceInterface.cast(interceptor);
072: }
073:
074: private Class createInterceptorClass(Class serviceInterface,
075: String serviceId) {
076: ClassFab cf = _classFactory.newClass(serviceInterface);
077:
078: cf.addField("_delegate", Modifier.PRIVATE | Modifier.FINAL,
079: serviceInterface);
080: cf.addField("_logger", Modifier.PRIVATE | Modifier.FINAL,
081: ServiceLogger.class);
082:
083: cf.addConstructor(new Class[] { serviceInterface,
084: ServiceLogger.class }, null,
085: "{ _delegate = $1; _logger = $2; }");
086:
087: addMethods(cf, serviceInterface, serviceId);
088:
089: return cf.createClass();
090: }
091:
092: private void addMethods(ClassFab cf, Class serviceInterface,
093: String serviceId) {
094: MethodIterator mi = new MethodIterator(serviceInterface);
095:
096: while (mi.hasNext())
097: addMethod(cf, mi.next());
098:
099: if (!mi.getToString())
100: cf.addToString(ServiceMessages.loggingInterceptor(
101: serviceId, serviceInterface));
102: }
103:
104: private void addMethod(ClassFab cf, MethodSignature signature) {
105: String name = '"' + signature.getName() + '"';
106: Class returnType = signature.getReturnType();
107: boolean isVoid = returnType.equals(void.class);
108:
109: // We'll see how well Javassist handles void methods with this setup
110:
111: BodyBuilder builder = new BodyBuilder();
112: builder.begin();
113: builder.addln("boolean debug = _logger.isDebugEnabled();");
114:
115: builder.addln("if (debug)");
116: builder.addln(" _logger.entry(%s, $args);", name);
117:
118: builder.addln("try");
119: builder.begin();
120:
121: if (!isVoid)
122: builder.add("%s result = ", toJavaClassName(returnType));
123:
124: builder.addln("_delegate.%s($$);", signature.getName());
125:
126: if (isVoid) {
127: builder.addln("if (debug)");
128: builder.addln(format(" _logger.voidExit(%s);", name));
129: builder.addln("return;");
130: } else {
131: builder.addln("if (debug)");
132: builder.addln(format(" _logger.exit(%s, ($w)result);",
133: name));
134: builder.addln("return result;");
135: }
136:
137: builder.end(); // try
138:
139: // Now, a catch for each declared exception (if any)
140:
141: if (signature.getExceptionTypes() != null)
142: for (Class exceptionType : signature.getExceptionTypes())
143: addExceptionHandler(builder, name, exceptionType);
144:
145: // And a catch for RuntimeException
146:
147: addExceptionHandler(builder, name, RuntimeException.class);
148:
149: builder.end();
150:
151: cf.addMethod(Modifier.PUBLIC, signature, builder.toString());
152: }
153:
154: private void addExceptionHandler(BodyBuilder builder,
155: String quotedMethodName, Class exceptionType) {
156: builder.addln("catch (%s ex)", exceptionType.getName());
157: builder.begin();
158: builder.addln("if (debug)");
159: builder.addln(" _logger.fail(%s, ex);", quotedMethodName);
160: builder.addln("throw ex;");
161: builder.end();
162: }
163: }
|