KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > hivemind > service > impl > LoggingInterceptorClassFactory


1 // Copyright 2004, 2005 The Apache Software Foundation
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15 package org.apache.hivemind.service.impl;
16
17 import java.lang.reflect.Modifier JavaDoc;
18 import java.util.Iterator JavaDoc;
19 import java.util.List JavaDoc;
20
21 import org.apache.commons.logging.Log;
22 import org.apache.hivemind.InterceptorStack;
23 import org.apache.hivemind.methodmatch.MethodMatcher;
24 import org.apache.hivemind.service.BodyBuilder;
25 import org.apache.hivemind.service.ClassFab;
26 import org.apache.hivemind.service.ClassFabUtils;
27 import org.apache.hivemind.service.ClassFactory;
28 import org.apache.hivemind.service.MethodContribution;
29 import org.apache.hivemind.service.MethodFab;
30 import org.apache.hivemind.service.MethodIterator;
31 import org.apache.hivemind.service.MethodSignature;
32
33 /**
34  * Factory for creation of interceptor classes that add logging capability to a service.
35  * The logging is based upon the Jakarta
36  * <a HREF="http://jakarta.apache.org/commons/logging.html">commons-logging</a> toolkit,
37  * which makes it very transportable.
38  *
39  * <p>
40  * The interceptor will log entry to each method and exit from the method
41  * (with return value), plus log any exceptions thrown by the method.
42  * The logger used is the <em>id of the service</em>, which is not necessarily
43  * the name of the implementing class. Logging occurs at the debug level.
44  *
45  * @author Howard Lewis Ship
46  */

47 public class LoggingInterceptorClassFactory
48 {
49     private ClassFactory _factory;
50
51     public LoggingInterceptorClassFactory(ClassFactory factory)
52     {
53         _factory = factory;
54     }
55
56     /**
57      * Creates a method that delegates to the _delegate object; this is used for
58      * methods that are not logged.
59      */

60     private void addPassThruMethodImplementation(ClassFab classFab, MethodSignature sig)
61     {
62         BodyBuilder builder = new BodyBuilder();
63         builder.begin();
64
65         builder.add("return ($r) _delegate.");
66         builder.add(sig.getName());
67         builder.addln("($$);");
68
69         builder.end();
70
71         classFab.addMethod(Modifier.PUBLIC, sig, builder.toString());
72     }
73
74     protected void addServiceMethodImplementation(ClassFab classFab, MethodSignature sig)
75     {
76         Class JavaDoc returnType = sig.getReturnType();
77         String JavaDoc methodName = sig.getName();
78
79         boolean isVoid = (returnType == void.class);
80
81         BodyBuilder builder = new BodyBuilder();
82
83         builder.begin();
84         builder.addln("boolean debug = _log.isDebugEnabled();");
85
86         builder.addln("if (debug)");
87         builder.add(" org.apache.hivemind.service.impl.LoggingUtils.entry(_log, ");
88         builder.addQuoted(methodName);
89         builder.addln(", $args);");
90
91         if (!isVoid)
92         {
93             builder.add(ClassFabUtils.getJavaClassName(returnType));
94             builder.add(" result = ");
95         }
96
97         builder.add("_delegate.");
98         builder.add(methodName);
99         builder.addln("($$);");
100
101         if (isVoid)
102         {
103             builder.addln("if (debug)");
104             builder.add(" org.apache.hivemind.service.impl.LoggingUtils.voidExit(_log, ");
105             builder.addQuoted(methodName);
106             builder.addln(");");
107         }
108         else
109         {
110             builder.addln("if (debug)");
111             builder.add(" org.apache.hivemind.service.impl.LoggingUtils.exit(_log, ");
112             builder.addQuoted(methodName);
113             builder.addln(", ($w)result);");
114             builder.addln("return result;");
115         }
116
117         builder.end();
118
119         MethodFab methodFab = classFab.addMethod(Modifier.PUBLIC, sig, builder.toString());
120
121         builder.clear();
122
123         builder.begin();
124         builder.add("org.apache.hivemind.service.impl.LoggingUtils.exception(_log, ");
125         builder.addQuoted(methodName);
126         builder.addln(", $e);");
127         builder.addln("throw $e;");
128         builder.end();
129
130         String JavaDoc body = builder.toString();
131
132         Class JavaDoc[] exceptions = sig.getExceptionTypes();
133
134         int count = exceptions.length;
135
136         for (int i = 0; i < count; i++)
137         {
138             methodFab.addCatch(exceptions[i], body);
139         }
140
141         // Catch and log any runtime exceptions, in addition to the
142
// checked exceptions.
143

144         methodFab.addCatch(RuntimeException JavaDoc.class, body);
145     }
146
147     protected void addServiceMethods(InterceptorStack stack, ClassFab fab, List JavaDoc parameters)
148     {
149         MethodMatcher matcher = buildMethodMatcher(parameters);
150
151         MethodIterator mi = new MethodIterator(stack.getServiceInterface());
152
153         while (mi.hasNext())
154         {
155             MethodSignature sig = mi.next();
156
157             if (includeMethod(matcher, sig))
158                 addServiceMethodImplementation(fab, sig);
159             else
160                 addPassThruMethodImplementation(fab, sig);
161         }
162
163         if (!mi.getToString())
164             addToStringMethod(stack, fab);
165     }
166
167     /**
168      * Creates a toString() method that identify the interceptor service id,
169      * the intercepted service id, and the service interface class name).
170      */

171     protected void addToStringMethod(InterceptorStack stack, ClassFab fab)
172     {
173         ClassFabUtils.addToStringMethod(
174             fab,
175             "<LoggingInterceptor for "
176                 + stack.getServiceExtensionPointId()
177                 + "("
178                 + stack.getServiceInterface().getName()
179                 + ")>");
180
181     }
182
183     private MethodMatcher buildMethodMatcher(List JavaDoc parameters)
184     {
185         MethodMatcher result = null;
186
187         Iterator JavaDoc i = parameters.iterator();
188         while (i.hasNext())
189         {
190             MethodContribution mc = (MethodContribution) i.next();
191
192             if (result == null)
193                 result = new MethodMatcher();
194
195             result.put(mc.getMethodPattern(), mc);
196         }
197
198         return result;
199     }
200
201     /**
202      * Creates the interceptor class.
203      */

204     public Class JavaDoc constructInterceptorClass(InterceptorStack stack, List JavaDoc parameters)
205     {
206         Class JavaDoc serviceInterfaceClass = stack.getServiceInterface();
207         
208         String JavaDoc name = ClassFabUtils.generateClassName(serviceInterfaceClass);
209
210         ClassFab classFab = _factory.newClass(name, Object JavaDoc.class);
211
212         classFab.addInterface(serviceInterfaceClass);
213
214         createInfrastructure(stack, classFab);
215
216         addServiceMethods(stack, classFab, parameters);
217
218         return classFab.createClass();
219     }
220
221     private void createInfrastructure(InterceptorStack stack, ClassFab classFab)
222     {
223         Class JavaDoc topClass = ClassFabUtils.getInstanceClass(classFab, stack.peek(), stack.getServiceInterface());
224         
225         classFab.addField("_log", Log.class);
226
227         // This is very important: since we know the instance of the top object (the next
228
// object in the pipeline for this service), we can build the instance variable
229
// and constructor to use the exact class rather than the service interface.
230
// That's more efficient at runtime, lowering the cost of using interceptors.
231
// One of the reasons I prefer Javassist over JDK Proxies.
232

233         classFab.addField("_delegate", topClass);
234
235         classFab.addConstructor(
236             new Class JavaDoc[] { Log.class, topClass },
237             null,
238             "{ _log = $1; _delegate = $2; }");
239     }
240
241     private boolean includeMethod(MethodMatcher matcher, MethodSignature sig)
242     {
243         if (matcher == null)
244             return true;
245
246         MethodContribution mc = (MethodContribution) matcher.get(sig);
247
248         return mc == null || mc.getInclude();
249     }
250
251 }
252
Popular Tags