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.hiveutils.service.impl; |
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.Iterator; |
22 | import java.util.List; |
23 | |
24 | import org.apache.commons.logging.Log; |
25 | |
26 | import org.apache.hivemind.InterceptorStack; |
27 | import org.apache.hivemind.ServiceInterceptorFactory; |
28 | import org.apache.hivemind.internal.Module; |
29 | |
30 | import net.sourceforge.hiveutils.service.ExceptionMapper; |
31 | import net.sourceforge.hiveutils.util.ClassMatcher; |
32 | |
33 | /** |
34 | * This service creates service interceptors that allow to map an exception to |
35 | * another. |
36 | * |
37 | * @author Jean-Francois Poilpret |
38 | */ |
39 | public class ExceptionMappingInterceptorFactory implements ServiceInterceptorFactory |
40 | { |
41 | public void createInterceptor(InterceptorStack stack, Module invokingMod, List parameters) |
42 | { |
43 | Log logger = stack.getServiceLog(); |
44 | stack.push(Proxy.newProxyInstance( |
45 | invokingMod.getClassResolver().getClassLoader(), |
46 | new Class[] {stack.getServiceInterface()}, |
47 | new Interceptor(logger, stack.peek(), parameters))); |
48 | } |
49 | |
50 | static private class Interceptor implements InvocationHandler |
51 | { |
52 | public Interceptor(Log logger, Object target, List parameters) |
53 | { |
54 | _logger = logger; |
55 | _target = target; |
56 | _mapper = null; |
57 | |
58 | // matcher will contain all exception mappings contained in parameters |
59 | ClassMatcher matcher = new ClassMatcher(); |
60 | boolean hasMapping = configureMapper(parameters, matcher); |
61 | |
62 | if (_mapper == null && !hasMapping) |
63 | { |
64 | // Log warning (we can go on but all exceptions will be passed |
65 | // "as is" |
66 | _logger.warn("No exception mapping defined"); |
67 | } |
68 | // Create the exception mapper from parameters and instantiate |
69 | // the exception-mapping proxy around the service |
70 | if (_mapper == null) |
71 | { |
72 | _mapper = new DefaultExceptionMapper(logger, matcher); |
73 | } |
74 | } |
75 | |
76 | private boolean configureMapper(List parameters, ClassMatcher matcher) |
77 | { |
78 | // matcher will contain all exception mappings contained in parameters |
79 | boolean hasMapping = false; |
80 | Iterator i = parameters.iterator(); |
81 | while (i.hasNext()) |
82 | { |
83 | Object param = i.next(); |
84 | if (param instanceof ExceptionMappingContribution) |
85 | { |
86 | if (_mapper == null) |
87 | { |
88 | ExceptionMappingContribution map = |
89 | (ExceptionMappingContribution) param; |
90 | // Check that from and to are Throwable |
91 | boolean classesAreExceptions = true; |
92 | if (!Throwable.class.isAssignableFrom(map.getFrom())) |
93 | { |
94 | // This is only a warning because we do not account for them |
95 | _logger.warn(map.getFrom().getName() + " is not a Throwable"); |
96 | classesAreExceptions = false; |
97 | } |
98 | if (!Throwable.class.isAssignableFrom(map.getTo())) |
99 | { |
100 | // This is only a warning because we do not account for them |
101 | _logger.warn(map.getTo().getName() + " is not a Throwable"); |
102 | classesAreExceptions = false; |
103 | } |
104 | if (classesAreExceptions) |
105 | { |
106 | hasMapping = true; |
107 | matcher.put(map.getFrom(), map.getTo()); |
108 | } |
109 | } |
110 | else |
111 | { |
112 | // Log warning, but don't account for this "exception-mapping" tag |
113 | _logger.warn("<exception-mapping> not accounted for because it " + |
114 | "conflicts with a <exception-mapper> tag"); |
115 | } |
116 | } |
117 | else if (param instanceof ExceptionMapperContribution) |
118 | { |
119 | if (_mapper == null && !hasMapping) |
120 | { |
121 | ExceptionMapperContribution map = |
122 | (ExceptionMapperContribution) param; |
123 | _mapper = map.getMapper(); |
124 | } |
125 | else |
126 | { |
127 | // Log warning, but don't account for this "exception-mapper" tag |
128 | _logger.warn("<exception-mapper> not accounted for because it " + |
129 | "conflicts with another tag"); |
130 | } |
131 | } |
132 | } |
133 | return hasMapping; |
134 | } |
135 | |
136 | //CSOFF: IllegalThrowsCheck |
137 | //CSOFF: IllegalCatchCheck |
138 | public Object invoke(Object proxy, Method method, Object[] args) |
139 | throws Throwable |
140 | { |
141 | try |
142 | { |
143 | return method.invoke(_target, args); |
144 | } |
145 | catch (InvocationTargetException e) |
146 | { |
147 | throw _mapper.translate(e.getTargetException()); |
148 | } |
149 | catch (Throwable t) |
150 | { |
151 | throw _mapper.translate(t); |
152 | } |
153 | } |
154 | //CSON: IllegalCatchCheck |
155 | //CSON: IllegalThrowsCheck |
156 | |
157 | private final Log _logger; |
158 | private final Object _target; |
159 | private ExceptionMapper _mapper; |
160 | } |
161 | } |