View Javadoc

1   /*
2    * EL4J, the Extension Library for the J2EE, adds incremental enhancements to
3    * the spring framework, http://el4j.sf.net
4    * Copyright (C) 2009 by ELCA Informatique SA, Av. de la Harpe 22-24,
5    * 1000 Lausanne, Switzerland, http://www.elca.ch
6    *
7    * EL4J is published under the GNU Lesser General Public License (LGPL)
8    * Version 2.1. See http://www.gnu.org/licenses/
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13   * GNU Lesser General Public License for more details.
14   *
15   * For alternative licensing, please contact info@elca.ch
16   */
17  package ch.elca.el4j.util.socketstatistics;
18  
19  import java.lang.reflect.Constructor;
20  import java.lang.reflect.Field;
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.Method;
23  
24  /**
25   * Delegator for the use of SocketImpl using reflection.
26   *
27   * Inspired by http://www.javaspecialists.eu/archive/Issue168.html
28   *
29   * @svnLink $Revision: 3929 $;$Date: 2009-09-25 16:38:41 +0200 (Fr, 25. Sep 2009) $;$Author: jonasha $;$URL: https://el4j.svn.sourceforge.net/svnroot/el4j/branches/el4j_3_1/el4j/framework/modules/socketstatistics/src/main/java/ch/elca/el4j/util/socketstatistics/ReflectiveDelegator.java $
30   *
31   * @author Jonas Hauenstein (JHN)
32   */
33  public class ReflectiveDelegator {
34  
35  	/**
36  	 * Source class of delegation.
37  	 */
38  	private final Object m_source;
39  
40  	/**
41  	 * Target calls of delegation.
42  	 */
43  	private final Object m_delegate;
44  
45  	/**
46  	 * Superclass for delegation-calls.
47  	 */
48  	private final Class m_superclass;
49  
50  	/**
51  	 * Constructor.
52  	 * 
53  	 * @param source
54  	 *            source class for reflection
55  	 * @param superclass
56  	 *            class used for delegation
57  	 * @param delegate
58  	 *            reference to class for delegated method calls
59  	 */
60  	public ReflectiveDelegator(Object source, Class superclass, Object delegate) {
61  		this.m_source = source;
62  		this.m_superclass = superclass;
63  		this.m_delegate = delegate;
64  	}
65  
66  	/**
67  	 * Constructor.
68  	 * 
69  	 * @param source
70  	 *            source class for reflection
71  	 * @param superclass
72  	 *            class used for delegation
73  	 * @param delegateClassName
74  	 *            name of the class for delegated method calls
75  	 */
76  	public ReflectiveDelegator(Object source, Class superclass, String delegateClassName) {
77  		try {
78  			this.m_source = source;
79  			this.m_superclass = superclass;
80  			Class implCl = Class.forName(delegateClassName);
81  			Constructor delegateConstructor = implCl.getDeclaredConstructor();
82  			delegateConstructor.setAccessible(true);
83  			this.m_delegate = delegateConstructor.newInstance();
84  		} catch (RuntimeException e) {
85  			throw e;
86  		} catch (Exception e) {
87  			throw new DelegationException("Could not make delegate object", e);
88  		}
89  	}
90  
91  	/**
92  	 * Method for invocation on delegated class.
93  	 * 
94  	 * @param <T>
95  	 *            generic return type
96  	 * @param args
97  	 *            arguments to be passed to the original method
98  	 * @return return value of original method
99  	 */
100 	public final <T> T invoke(Object... args) {
101 		try {
102 			String methodName = extractMethodName();
103 			Method method = findMethod(methodName, args);
104 			@SuppressWarnings("unchecked")
105 			T t = (T) invoke0(method, args);
106 			return t;
107 		} catch (NoSuchMethodException e) {
108 			throw new DelegationException(e);
109 		}
110 	}
111 
112 	/**
113 	 * Internal implementation for invocation on delegated class.
114 	 * 
115 	 * @param method
116 	 *            method to be called
117 	 * @param args
118 	 *            arguments to be passed to the called method
119 	 * @return return value of original method
120 	 */
121 	private Object invoke0(Method method, Object[] args) {
122 		try {
123 			writeFields(m_superclass, m_source, m_delegate);
124 			method.setAccessible(true);
125 			Object result = method.invoke(m_delegate, args);
126 			writeFields(m_superclass, m_delegate, m_source);
127 			return result;
128 		} catch (RuntimeException e) {
129 			throw e;
130 		} catch (InvocationTargetException e) {
131 			throw new DelegationException(e.getCause());
132 		} catch (Exception e) {
133 			throw new DelegationException(e);
134 		}
135 	}
136 
137 	/**
138 	 * @param clazz
139 	 * @param from
140 	 * @param to
141 	 * @throws Exception
142 	 */
143 	private void writeFields(Class clazz, Object from, Object to) throws Exception {
144 		for (Field field : clazz.getDeclaredFields()) {
145 			field.setAccessible(true);
146 			field.set(to, field.get(from));
147 		}
148 	}
149 
150 	/**
151 	 * Returns name of calling method using stacktrace.
152 	 * 
153 	 * @return name of the calling method
154 	 */
155 	private String extractMethodName() {
156 		Throwable t = new Throwable();
157 		String methodName = t.getStackTrace()[2].getMethodName();
158 		return methodName;
159 	}
160 
161 	/**
162 	 * Search for method with matching name and signature.
163 	 * 
164 	 * @param methodName
165 	 *            name of the method to find for delegated call
166 	 * @param args
167 	 *            list of arguments the found method has to take
168 	 * @return found method
169 	 * @throws NoSuchMethodException
170 	 *             thrown if no matching method found for delegation
171 	 */
172 	private Method findMethod(String methodName, Object[] args) throws NoSuchMethodException {
173 		Class<?> clazz = m_superclass;
174 		if (args.length == 0) {
175 			return clazz.getDeclaredMethod(methodName);
176 		}
177 		Method match = null;
178 		// search inside declared class methods
179 		next: for (Method method : clazz.getDeclaredMethods()) {
180 			if (method.getName().equals(methodName)) {
181 				Class<?>[] classes = method.getParameterTypes();
182 				if (classes.length == args.length) {
183 					for (int i = 0; i < classes.length; i++) {
184 						Class<?> argType = classes[i];
185 						argType = convertPrimitiveClass(argType);
186 						if (!argType.isInstance(args[i])) {
187 							continue next;
188 						}
189 					}
190 					if (match == null) {
191 						match = method;
192 					} else {
193 						throw new DelegationException("Duplicate matches");
194 					}
195 				}
196 			}
197 		}
198 		if (match != null) {
199 			return match;
200 		}
201 
202 		// if no fitting method found, also search inside inherited methods
203 		next: for (Method method : clazz.getMethods()) {
204 			if (method.getName().equals(methodName)) {
205 				Class<?>[] classes = method.getParameterTypes();
206 				if (classes.length == args.length) {
207 					for (int i = 0; i < classes.length; i++) {
208 						Class<?> argType = classes[i];
209 						argType = convertPrimitiveClass(argType);
210 						if (!argType.isInstance(args[i])) {
211 							continue next;
212 						}
213 					}
214 					if (match == null) {
215 						match = method;
216 					} else {
217 						throw new DelegationException("Duplicate matches");
218 					}
219 				}
220 			}
221 		}
222 		if (match != null) {
223 			return match;
224 		}
225 
226 		// no matching method found
227 		throw new DelegationException("Could not find method: " + methodName);
228 	}
229 
230 	/**
231 	 * Converts primitives to corresponding java class definitions.
232 	 * 
233 	 * @param primitive
234 	 *            primitive for conversion
235 	 * @return corresponding java class
236 	 */
237 	private Class<?> convertPrimitiveClass(Class<?> primitive) {
238 		if (primitive.isPrimitive()) {
239 			if (primitive == int.class) {
240 				return Integer.class;
241 			}
242 			if (primitive == boolean.class) {
243 				return Boolean.class;
244 			}
245 			if (primitive == float.class) {
246 				return Float.class;
247 			}
248 			if (primitive == long.class) {
249 				return Long.class;
250 			}
251 			if (primitive == double.class) {
252 				return Double.class;
253 			}
254 			if (primitive == short.class) {
255 				return Short.class;
256 			}
257 			if (primitive == byte.class) {
258 				return Byte.class;
259 			}
260 			if (primitive == char.class) {
261 				return Character.class;
262 			}
263 		}
264 		return primitive;
265 	}
266 
267 	/**
268 	 * Method for explicit delegation of method call.
269 	 * 
270 	 * @param methodName
271 	 *            name of method to be called
272 	 * @param parameters
273 	 *            parameters to be passed to called method
274 	 * @return return value of called method
275 	 */
276 	public DelegatorMethodFinder delegateTo(String methodName, Class<?>... parameters) {
277 		return new DelegatorMethodFinder(methodName, parameters);
278 	}
279 
280 	/**
281 	 * Internal class for explicitly defined method calls using delegateTo.
282 	 */
283 	public class DelegatorMethodFinder {
284 		/**
285 		 * Method to be used for call.
286 		 */
287 		private final Method method;
288 
289 		/**
290 		 * Constructor.
291 		 * 
292 		 * @param methodName
293 		 *            name of the method to find for delegated call
294 		 * @param parameterTypes
295 		 *            signature of method to be called
296 		 */
297 		public DelegatorMethodFinder(String methodName, Class<?>... parameterTypes) {
298 			try {
299 				method = m_superclass.getDeclaredMethod(methodName, parameterTypes);
300 			} catch (RuntimeException e) {
301 				throw e;
302 			} catch (Exception e) {
303 				throw new DelegationException(e);
304 			}
305 		}
306 
307 		/**
308 		 * Method for invocation on delegated class.
309 		 * 
310 		 * @param <T>
311 		 *            generic return type
312 		 * @param parameters
313 		 *            arguments to be passed to the original method
314 		 * @return return value of original method
315 		 */
316 		public <T> T invoke(Object... parameters) {
317 			@SuppressWarnings("unchecked")
318 			T t = (T) ReflectiveDelegator.this.invoke0(method, parameters);
319 			return t;
320 		}
321 	}
322 }