1 | // Copyright 2004-2007 Jean-Francois Poilpret |
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 net.sourceforge.hivetranse.transaction; |
16 | |
17 | import java.lang.reflect.InvocationHandler; |
18 | import java.lang.reflect.InvocationTargetException; |
19 | import java.lang.reflect.Method; |
20 | import java.lang.reflect.Proxy; |
21 | import java.util.List; |
22 | |
23 | import org.apache.commons.logging.Log; |
24 | |
25 | import org.apache.hivemind.ApplicationRuntimeException; |
26 | import org.apache.hivemind.InterceptorStack; |
27 | import org.apache.hivemind.ServiceInterceptorFactory; |
28 | import org.apache.hivemind.internal.Module; |
29 | import org.apache.hivemind.methodmatch.MethodMatcher; |
30 | import org.apache.hivemind.service.MethodSignature; |
31 | |
32 | import net.sourceforge.hiveutils.util.ClassMatcher; |
33 | |
34 | /** |
35 | * This service creates service interceptors that manage transactions. |
36 | * <p> |
37 | * Each interceptor, depending on its configuration for the given service, will |
38 | * manage transaction creation/commit/rollback upon situations. |
39 | * |
40 | * @author Jean-Francois Poilpret |
41 | */ |
42 | public class TransactionInterceptorFactory implements ServiceInterceptorFactory |
43 | { |
44 | public TransactionInterceptorFactory( Log logger, |
45 | TransactionService manager, |
46 | List defaults) |
47 | { |
48 | _manager = manager; |
49 | // Create default method matcher and exception matchers |
50 | _defaultMethodMatcher = new MethodMatcher(); |
51 | _defaultExcMatcher = new ClassMatcher(); |
52 | if (defaults != null && defaults.size() > 1) |
53 | { |
54 | logger.error("<init> defaults.size > 1"); |
55 | throw new ApplicationRuntimeException("defaults.size must be zero or one"); |
56 | } |
57 | |
58 | if (defaults != null && defaults.size() == 1) |
59 | { |
60 | TransactionSettingsContribution settings = |
61 | (TransactionSettingsContribution) defaults.get(0); |
62 | setMatchers(settings, _defaultMethodMatcher, _defaultExcMatcher); |
63 | } |
64 | // Add default demarcation (in case defaults did not specify it) |
65 | _defaultMethodMatcher.put("*", TransactionDemarcation.Never); |
66 | // Add default demarcation (in case defaults did not specify it) |
67 | _defaultExcMatcher.put(Throwable.class, Boolean.TRUE); |
68 | } |
69 | |
70 | static private void setMatchers(TransactionSettingsContribution parameters, |
71 | MethodMatcher methods, |
72 | ClassMatcher exceptions) |
73 | { |
74 | TransactionContribution[] demarcations = parameters.getTransactionContributions(); |
75 | for (int i = 0; i < demarcations.length; i++) |
76 | { |
77 | methods.put(demarcations[i].getPattern(), |
78 | demarcations[i].getDemarcation()); |
79 | } |
80 | RollbackExceptionContribution[] exceptionContribs = |
81 | parameters.getRollbackExceptionContributions(); |
82 | for (int i = 0; i < exceptionContribs.length; i++) |
83 | { |
84 | exceptions.put( exceptionContribs[i].getException(), |
85 | Boolean.valueOf(exceptionContribs[i].getRollback())); |
86 | } |
87 | } |
88 | |
89 | public void createInterceptor( InterceptorStack stack, |
90 | Module invokingMod, |
91 | List parameters) |
92 | { |
93 | Log logger = stack.getServiceLog(); |
94 | TransactionSettingsContribution settings = null; |
95 | if (parameters != null && parameters.size() == 1) |
96 | { |
97 | settings = (TransactionSettingsContribution) parameters.get(0); |
98 | } |
99 | stack.push(Proxy.newProxyInstance( |
100 | invokingMod.getClassResolver().getClassLoader(), |
101 | new Class[] {stack.getServiceInterface()}, |
102 | new Interceptor(logger, stack.peek(), settings))); |
103 | } |
104 | |
105 | private class Interceptor implements InvocationHandler |
106 | { |
107 | public Interceptor( Log logger, |
108 | Object target, |
109 | TransactionSettingsContribution settings) |
110 | { |
111 | _logger = logger; |
112 | _target = target; |
113 | // Create method matcher for finding demarcation to use for a method |
114 | _methodMatcher = new MethodMatcher(); |
115 | // Create exception matcher for finding if an exception is a rollbacker |
116 | _excMatcher = new ClassMatcher(); |
117 | if (settings != null) |
118 | { |
119 | setMatchers(settings, _methodMatcher, _excMatcher); |
120 | } |
121 | } |
122 | |
123 | //CSOFF: IllegalThrowsCheck |
124 | //CSOFF: IllegalCatchCheck |
125 | public Object invoke(Object proxy, Method method, Object[] args) |
126 | throws Throwable |
127 | { |
128 | // First of all, find demarcation for method |
129 | TransactionDemarcation demarcation = getDemarcation(method); |
130 | |
131 | _manager.begin(demarcation); |
132 | try |
133 | { |
134 | return method.invoke(_target, args); |
135 | } |
136 | catch (IllegalAccessException e) |
137 | { |
138 | // Can never happen (normally!) |
139 | _logger.fatal("Unexpected", e); |
140 | exceptionCaught(e); |
141 | throw e; |
142 | } |
143 | catch (InvocationTargetException e) |
144 | { |
145 | // Check if this exception must rollback the current transaction |
146 | exceptionCaught(e.getTargetException()); |
147 | throw e.getTargetException(); |
148 | } |
149 | catch (Throwable e) |
150 | { |
151 | // Can never happen (normally!) |
152 | _logger.fatal("Unexpected", e); |
153 | // Check if this exception must rollback the current transaction |
154 | exceptionCaught(e); |
155 | throw e; |
156 | } |
157 | finally |
158 | { |
159 | _manager.end(); |
160 | } |
161 | } |
162 | //CSON: IllegalCatchCheck |
163 | //CSON: IllegalThrowsCheck |
164 | |
165 | private TransactionDemarcation getDemarcation(Method method) |
166 | { |
167 | MethodSignature signature = new MethodSignature(method); |
168 | TransactionDemarcation demarcation; |
169 | demarcation = (TransactionDemarcation) _methodMatcher.get(signature); |
170 | if (demarcation != null) |
171 | { |
172 | return demarcation; |
173 | } |
174 | return (TransactionDemarcation) _defaultMethodMatcher.get(signature); |
175 | } |
176 | |
177 | private void exceptionCaught(Throwable e) |
178 | { |
179 | if ( mustRollback(e) |
180 | && (_manager.getCurrentTransaction() != null)) |
181 | { |
182 | _manager.setRollbackOnly(); |
183 | } |
184 | } |
185 | |
186 | private boolean mustRollback(Throwable e) |
187 | { |
188 | Boolean rollback; |
189 | rollback = (Boolean) _excMatcher.get(e); |
190 | if (rollback == null) |
191 | { |
192 | rollback = (Boolean) _defaultExcMatcher.get(e); |
193 | } |
194 | if (rollback == null) |
195 | { |
196 | return true; |
197 | } |
198 | return rollback.booleanValue(); |
199 | } |
200 | |
201 | private final Log _logger; |
202 | private final Object _target; |
203 | private final MethodMatcher _methodMatcher; |
204 | private final ClassMatcher _excMatcher; |
205 | } |
206 | |
207 | private final TransactionService _manager; |
208 | private final MethodMatcher _defaultMethodMatcher; |
209 | private final ClassMatcher _defaultExcMatcher; |
210 | } |