Mixer.java

biz/hammurapi/convert/Mixer.java

Violations

Inspector Message Severity Location
Java Inspector 048 Copyrights information should be present in each file. 1
Java Inspector 049 Use a Collection instead of arrays Object[] 2 51:22
Java Inspector 049 Use a Collection instead of arrays Object[] 2 52:22
Java Inspector 049 Use a Collection instead of arrays Object[] 2 83:55
Java Inspector 049 Use a Collection instead of arrays Object[] 2 87:62
Java Inspector 049 Use a Collection instead of arrays Object[] 2 130:27
Java Inspector 049 Use a Collection instead of arrays Object[] 2 133:31
Java Inspector 049 Use a Collection instead of arrays Object[] 2 159:22
Java Inspector 049 Use a Collection instead of arrays Object[] 2 162:31
Java Inspector 049 Use a Collection instead of arrays Object[] 2 171:70
Java Inspector 049 Use a Collection instead of arrays Object[] 2 172:70
Java Inspector 049 Use a Collection instead of arrays Object[] 2 248:23
Java Inspector 049 Use a Collection instead of arrays Object[] 2 250:17
Java Inspector 049 Use a Collection instead of arrays Object[] 2 319:30
Java Inspector 068 Do not use System.out and System.err to output logging messages. Use loggers instead. 2 182:83
Java Inspector 070-A Cyclomatic complexity is too high: 14, maximum allowed is 12 2 124:9
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 154:25
Java Inspector 089 Parameter object is not documented 2 38:9
Java Inspector 089 Parameter interfaces is not documented 2 38:9
Java Inspector 089 Method return value is not properly documented 2 38:9
Java Inspector 089 Undocumented method 2 72:41
Java Inspector 089 Undocumented method 2 96:41
Java Inspector 089 Parameter master is not documented 2 124:9
Java Inspector 089 Parameter interceptors is not documented 2 124:9
Java Inspector 089 Method return value is not properly documented 2 124:9
Java Inspector 089 Undocumented method 2 197:41
Java Inspector 089 Undocumented method 2 206:41
Java Inspector 089 Undocumented parameter objects 2 222:9
Java Inspector 089 Parameter interfaces is not documented 2 222:9
Java Inspector 089 Javadoc contains tag for non-existent parameter object 2 222:9
Java Inspector 089 Method return value is not properly documented 2 222:9
Java Inspector 089 Undocumented method 2 269:41
Java Inspector 089 Parameter objects is not documented 2 300:9
Java Inspector 089 Method return value is not properly documented 2 300:9
Java Inspector 089 Undocumented method 2 340:57
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 182:84
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 182:120

Source code

1package biz.hammurapi.convert;
2
3import java.lang.reflect.InvocationHandler;
4import java.lang.reflect.InvocationTargetException;
5import java.lang.reflect.Method;
6import java.lang.reflect.Proxy;
7import java.util.ArrayList;
8import java.util.Arrays;
9import java.util.Collection;
10import java.util.HashMap;
11import java.util.Iterator;
12import java.util.List;
13import java.util.Map;
14
15import biz.hammurapi.wrap.WrapperHandler;
16
17
18/**
19 * Mixes interfaces from multiple objects into one proxy.
20 * @author Pavel
21 *
22 */
23public class Mixer {
24
25 /**
26 * Mixes-in implementation of interfaces into class. I.e. it creates a proxy
27 * which implements all classe's interfaces plus interfaces from parameters.
28 * E.g. class has methods compatible with interfaces, but doesn't implement
29 * the interface itself. This is very similar to duck typing.
30 * This method doesn't check for method compatibility, it simply routes
31 * interface methods to the object. It is useful when only a sub-set of interface
32 * methods is used by the calling code, so the code would work even if strict
33 * conversion failed.
34 * @param object
35 * @param interfaces
36 * @return
37 */
38 public static Object mixIn(final Object object, Class[] interfaces) {
39 List additional = new ArrayList();
40 for (int i = 0; interfaces!=null && i<interfaces.length; ++i) {
41 if (!interfaces[i].isInstance(object)) {
42 additional.add(interfaces[i]);
43 }
44 }
45
46 if (additional.isEmpty()) {
47 return object;
48 }
49
50 Class objectClass = object.getClass();
51 Class[] objectInterfaces = WrapperHandler.getClassInterfaces(objectClass);
52 Class[] proxyInterfaces = new Class[objectInterfaces.length+additional.size()];
53 ClassLoader classLoader = null;
54 for (int i=0; i<objectInterfaces.length; ++i) {
55 proxyInterfaces[i] = objectInterfaces[i];
56 classLoader = DuckConverterFactory.getChildClassLoader(classLoader, objectInterfaces[i].getClassLoader());
57 }
58
59 final Map methodMap = new HashMap();
60 Iterator it = additional.iterator();
61 for (int i=objectInterfaces.length; it.hasNext(); ++i) {
62 proxyInterfaces[i] = (Class) it.next();
63 classLoader = DuckConverterFactory.getChildClassLoader(classLoader, proxyInterfaces[i].getClassLoader());
64 DuckConverterFactory.duckMap(proxyInterfaces[i], objectClass, methodMap);
65 }
66
67 return Proxy.newProxyInstance(
68 classLoader == null ? objectClass.getClassLoader() : classLoader,
69 proxyInterfaces,
70 new FilterInvocationHandler() {
71
72 public Object invoke(
73 Object proxy,
74 Method method,
75 Object[] args) throws Throwable {
76
77 Method mappedMethod = (Method) methodMap.get(method);
78 if (mappedMethod ==null) {
79 return method.invoke(object, args);
80 }
81
82// CompositeConverter converter = CompositeConverter.getDefaultConverter();
83 Object[] convertedArgs;
84 if (args==null) {
85 convertedArgs = null;
86 } else {
87 Class[] parameterTypes = mappedMethod.getParameterTypes();
88 convertedArgs = new Object[args.length];
89 for (int i=0; i<convertedArgs.length; ++i) {
90 convertedArgs[i] = ConvertingService.convert(args[i], parameterTypes[i]);
91 }
92 }
93 return mappedMethod.invoke(object, args);
94 }
95
96 public Object getMaster() {
97 return object;
98 }
99
100 });
101 }
102
103 /**
104 * Creates a proxy which routes invocations to master unless one of interceptors
105 * has a method with matching signature. Only interface methods are routed
106 * to interceptors.
107 *
108 * Traditional approach to intercepting in Java is to create filters/wrappers.
109 * This approach might be problematic if the filter master instances might be of
110 * different subclasses of the base filtered type and if the subclassed functionality
111 * must be propagated through the filter.
112 *
113 * Returned proxy invokes only interceptor's method. It is responsiblity of the
114 * interceptor to call master's method if needed.
115 *
116 * Interceptor methods are matched to master methods based on name and parameter
117 * types equality (as in overriding). Return type and exception types are not
118 * taken in consideration.
119 *
120 * @param master
121 * @param interceptors
122 * @return
123 */
124 public static Object addInterceptors(final Object master, Object[] interceptors) {
125 if (master == null) {
126 return null;
127 }
128
129 // Array of collections.
130 Collection[] interceptorMethods = new Collection[interceptors.length];
131 for (int i=0; i<interceptors.length; ++i) {
132 Class interceptorClass = interceptors[i].getClass();
133 Method[] iMethods = interceptorClass.getMethods();
134 interceptorMethods[i] = new ArrayList();
135 for (int m=0; m<iMethods.length; ++m) {
136 Method interceptorMethod = iMethods[m];
137
138 if (!interceptorMethod.getDeclaringClass().equals(Object.class)) {
139 interceptorMethods[i].add(interceptorMethod);
140 }
141 }
142 }
143
144 class InterceptorEntry {
145 Object object;
146 Method method;
147
148 InterceptorEntry(Object object, Method method) {
149 super();
150 this.object = object;
151 this.method = method;
152 }
153
154 Object invoke(Object[] args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
155 return method.invoke(object, args);
156 }
157 }
158
159 Class[] interfaces = WrapperHandler.getClassInterfaces(master.getClass());
160 final Map interceptionMap = new HashMap();
161 for (int interfaceIdx=0; interfaceIdx<interfaces.length; ++interfaceIdx) {
162 Method[] methods = interfaces[interfaceIdx].getMethods();
163 Z: for (int methodIdx = 0; methodIdx < methods.length; ++methodIdx) {
164 Method masterMethod = methods[methodIdx];
165 if (!masterMethod.getDeclaringClass().equals(Object.class)) {
166 for (int interceptorIdx =0; interceptorIdx<interceptors.length; ++interceptorIdx) {
167 Iterator interceptorMethodIterator = interceptorMethods[interceptorIdx].iterator();
168 Y: while (interceptorMethodIterator.hasNext()) {
169 Method interceptorMethod = (Method) interceptorMethodIterator.next();
170 if (masterMethod.getName().equals(interceptorMethod.getName())) {
171 Class[] masterParameterTypes = masterMethod.getParameterTypes();
172 Class[] interceptorParameterTypes = interceptorMethod.getParameterTypes();
173 if (masterParameterTypes.length!=interceptorParameterTypes.length) {
174 continue;
175 }
176 for (int prmIdx = 0; prmIdx<masterParameterTypes.length; ++prmIdx) {
177 if (!masterParameterTypes[prmIdx].equals(interceptorParameterTypes[prmIdx])) {
178 continue Y;
179 }
180 }
181
182 System.out.println("~~~~~ Mapped method "+masterMethod+" to interceptor "+interceptorIdx);
183 interceptionMap.put(masterMethod, new InterceptorEntry(interceptors[interceptorIdx], interceptorMethod));
184 continue Z;
185 }
186 }
187 }
188 }
189 }
190 }
191
192 return Proxy.newProxyInstance(
193 Object.class.getClassLoader(),
194 interfaces,
195 new FilterInvocationHandler() {
196
197 public Object invoke(
198 Object proxy,
199 Method method,
200 Object[] args) throws Throwable {
201
202 InterceptorEntry ie = (InterceptorEntry) interceptionMap.get(method);
203 return ie==null ? method.invoke(master, args) : ie.invoke(args);
204 }
205
206 public Object getMaster() {
207 return master;
208 }
209
210 });
211
212 }
213
214 /**
215 * Creates a proxy which implements given interfaces and routes invocations
216 * to appropriate object. If object is not found for a particular interface,
217 * then invocation is routed to the first object.
218 * @param object
219 * @param interfaces
220 * @return
221 */
222 public static Object combine(final Object[] objects, Class[] interfaces) {
223 if (objects==null || objects.length==0) {
224 return null;
225 }
226
227 boolean firstDoesIt = true;
228 for (int i=0; i<interfaces.length; ++i) {
229 if (!interfaces[i].isInstance(objects[0])) {
230 firstDoesIt = false;
231 break;
232 }
233 }
234
235 if (firstDoesIt) {
236 return objects[0];
237 }
238
239 if (objects.length==1) {
240 return mixIn(objects[0], interfaces);
241 }
242
243 List allInterfaceMethods = new ArrayList();
244 for (int i=0; i<interfaces.length; ++i) {
245 allInterfaceMethods.addAll(Arrays.asList(interfaces[i].getMethods()));
246 }
247
248 Method[] aim = (Method[]) allInterfaceMethods.toArray(new Method[allInterfaceMethods.size()]);
249
250 final Map[] methodMaps = new Map[objects.length];
251 for (int i=0; i<objects.length; ++i) {
252 methodMaps[i] = new HashMap();
253 DuckConverterFactory.duckMap(aim, objects[i].getClass().getMethods(), methodMaps[i]);
254 }
255
256 ClassLoader classLoader = null;
257 for (int i=0; i<objects.length; ++i) {
258 classLoader = DuckConverterFactory.getChildClassLoader(classLoader, objects[i].getClass().getClassLoader());
259 }
260 for (int i=0; i<interfaces.length; ++i) {
261 classLoader = DuckConverterFactory.getChildClassLoader(classLoader, interfaces[i].getClassLoader());
262 }
263
264 return Proxy.newProxyInstance(
265 classLoader == null ? objects[0].getClass().getClassLoader() : classLoader,
266 interfaces,
267 new InvocationHandler() {
268
269 public Object invoke(
270 Object proxy,
271 Method method,
272 Object[] args) throws Throwable {
273
274 Class declaringClass = method.getDeclaringClass();
275 for (int i=0; i<objects.length; ++i) {
276 if (declaringClass.isInstance(objects[i])) {
277 return method.invoke(objects[i], args);
278 }
279 Method mappedMethod = (Method) methodMaps[i].get(method);
280 if (mappedMethod!=null) {
281 return mappedMethod.invoke(objects[i], args);
282 }
283 }
284
285 return method.invoke(objects[0], args);
286 }
287
288 });
289 }
290
291 /**
292 * Mixes interfaces from multiple objects into one proxy.
293 * It can be used to "mix-in" interface into another interface, e.g.
294 * mix-in application-specific interfaces into HttpServletRequest.
295 * Different interfaces can be mixed-in depending on context, which is
296 * difficult to achieve with wrapping.
297 * @param objects
298 * @return
299 */
300 public static Object mix(final Object[] objects) {
301 if (objects==null || objects.length==0) {
302 return null;
303 }
304
305 if (objects.length==1) {
306 return objects[0];
307 }
308
309
310 // Maps interface to implementation.
311 final Map interfaceMap = new HashMap();
312 List interfaces = new ArrayList();
313 ClassLoader classLoader = null;
314 boolean onlyFirstInterfaces = false;
315
316 for (int i=0; i<objects.length; ++i) {
317 Class sourceClass = objects[i].getClass();
318 classLoader=DuckConverterFactory.getChildClassLoader(classLoader, sourceClass.getClassLoader());
319 Class[] cia = WrapperHandler.getClassInterfaces(sourceClass);
320 for (int j=0; j<cia.length; ++j) {
321 Class classInterface = cia[j];
322 if (!interfaceMap.containsKey(classInterface)) {
323 onlyFirstInterfaces = i==0;
324 interfaceMap.put(classInterface, objects[i]);
325 interfaces.add(classInterface);
326 }
327 }
328 }
329
330 // All interfaces are implemented by the first object, i.e. other objects add nothing.
331 if (onlyFirstInterfaces) {
332 return objects[0];
333 }
334
335 return Proxy.newProxyInstance(
336 classLoader == null ? objects[0].getClass().getClassLoader() : classLoader,
337 (Class[]) interfaces.toArray(new Class[interfaces.size()]),
338 new InvocationHandler() {
339
340 public Object invoke(
341 Object proxy,
342 Method method,
343 Object[] args) throws Throwable {
344
345 Object target = interfaceMap.get(method.getDeclaringClass());
346 return method.invoke(target==null ? objects[0] : target, args);
347 }
348
349 });
350 }
351
352}
353