View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.core;
18  
19  import java.io.IOException;
20  import java.io.ObjectInputStream;
21  import java.io.Serializable;
22  import java.lang.reflect.Field;
23  import java.lang.reflect.GenericArrayType;
24  import java.lang.reflect.InvocationHandler;
25  import java.lang.reflect.InvocationTargetException;
26  import java.lang.reflect.Method;
27  import java.lang.reflect.ParameterizedType;
28  import java.lang.reflect.Proxy;
29  import java.lang.reflect.Type;
30  import java.lang.reflect.TypeVariable;
31  import java.lang.reflect.WildcardType;
32  
33  import org.springframework.util.Assert;
34  import org.springframework.util.ConcurrentReferenceHashMap;
35  import org.springframework.util.ReflectionUtils;
36  
37  /**
38   * Internal utility class that can be used to obtain wrapped {@link Serializable} variants
39   * of {@link java.lang.reflect.Type}s.
40   *
41   * <p>{@link #forField(Field) Fields} or {@link #forMethodParameter(MethodParameter)
42   * MethodParameters} can be used as the root source for a serializable type. Alternatively
43   * the {@link #forGenericSuperclass(Class) superclass},
44   * {@link #forGenericInterfaces(Class) interfaces} or {@link #forTypeParameters(Class)
45   * type parameters} or a regular {@link Class} can also be used as source.
46   *
47   * <p>The returned type will either be a {@link Class} or a serializable proxy of
48   * {@link GenericArrayType}, {@link ParameterizedType}, {@link TypeVariable} or
49   * {@link WildcardType}. With the exception of {@link Class} (which is final) calls to
50   * methods that return further {@link Type}s (for example
51   * {@link GenericArrayType#getGenericComponentType()}) will be automatically wrapped.
52   *
53   * @author Phillip Webb
54   * @since 4.0
55   */
56  abstract class SerializableTypeWrapper {
57  
58  	private static final Class<?>[] SUPPORTED_SERIALIZABLE_TYPES = {
59  			GenericArrayType.class, ParameterizedType.class, TypeVariable.class, WildcardType.class};
60  
61  	private static final Method EQUALS_METHOD = ReflectionUtils.findMethod(Object.class,
62  			"equals", Object.class);
63  
64  	private static final Method GET_TYPE_PROVIDER_METHOD = ReflectionUtils.findMethod(
65  			SerializableTypeProxy.class, "getTypeProvider");
66  
67  	private static final ConcurrentReferenceHashMap<Type, Type> cache =
68  			new ConcurrentReferenceHashMap<Type, Type>(256);
69  
70  	/**
71  	 * Return a {@link Serializable} variant of {@link Field#getGenericType()}.
72  	 */
73  	public static Type forField(Field field) {
74  		Assert.notNull(field, "Field must not be null");
75  		return forTypeProvider(new FieldTypeProvider(field));
76  	}
77  
78  	/**
79  	 * Return a {@link Serializable} variant of
80  	 * {@link MethodParameter#getGenericParameterType()}.
81  	 */
82  	public static Type forMethodParameter(MethodParameter methodParameter) {
83  		return forTypeProvider(new MethodParameterTypeProvider(methodParameter));
84  	}
85  
86  	/**
87  	 * Return a {@link Serializable} variant of {@link Class#getGenericSuperclass()}.
88  	 */
89  	@SuppressWarnings("serial")
90  	public static Type forGenericSuperclass(final Class<?> type) {
91  		return forTypeProvider(new DefaultTypeProvider() {
92  			@Override
93  			public Type getType() {
94  				return type.getGenericSuperclass();
95  			}
96  		});
97  	}
98  
99  	/**
100 	 * Return a {@link Serializable} variant of {@link Class#getGenericInterfaces()}.
101 	 */
102 	@SuppressWarnings("serial")
103 	public static Type[] forGenericInterfaces(final Class<?> type) {
104 		Type[] result = new Type[type.getGenericInterfaces().length];
105 		for (int i = 0; i < result.length; i++) {
106 			final int index = i;
107 			result[i] = forTypeProvider(new DefaultTypeProvider() {
108 				@Override
109 				public Type getType() {
110 					return type.getGenericInterfaces()[index];
111 				}
112 			});
113 		}
114 		return result;
115 	}
116 
117 	/**
118 	 * Return a {@link Serializable} variant of {@link Class#getTypeParameters()}.
119 	 */
120 	@SuppressWarnings("serial")
121 	public static Type[] forTypeParameters(final Class<?> type) {
122 		Type[] result = new Type[type.getTypeParameters().length];
123 		for (int i = 0; i < result.length; i++) {
124 			final int index = i;
125 			result[i] = forTypeProvider(new DefaultTypeProvider() {
126 				@Override
127 				public Type getType() {
128 					return type.getTypeParameters()[index];
129 				}
130 			});
131 		}
132 		return result;
133 	}
134 
135 	/**
136 	 * Unwrap the given type, effectively returning the original non-serializable type.
137 	 * @param type the type to unwrap
138 	 * @return the original non-serializable type
139 	 */
140 	@SuppressWarnings("unchecked")
141 	public static <T extends Type> T unwrap(T type) {
142 		Type unwrapped = type;
143 		while (unwrapped instanceof SerializableTypeProxy) {
144 			unwrapped = ((SerializableTypeProxy) type).getTypeProvider().getType();
145 		}
146 		return (T) unwrapped;
147 	}
148 
149 	/**
150 	 * Return a {@link Serializable} {@link Type} backed by a {@link TypeProvider} .
151 	 */
152 	static Type forTypeProvider(final TypeProvider provider) {
153 		Assert.notNull(provider, "Provider must not be null");
154 		if (provider.getType() instanceof Serializable || provider.getType() == null) {
155 			return provider.getType();
156 		}
157 		Type cached = cache.get(provider.getType());
158 		if (cached != null) {
159 			return cached;
160 		}
161 		for (Class<?> type : SUPPORTED_SERIALIZABLE_TYPES) {
162 			if (type.isAssignableFrom(provider.getType().getClass())) {
163 				ClassLoader classLoader = provider.getClass().getClassLoader();
164 				Class<?>[] interfaces = new Class<?>[] { type,
165 					SerializableTypeProxy.class, Serializable.class };
166 				InvocationHandler handler = new TypeProxyInvocationHandler(provider);
167 				cached = (Type) Proxy.newProxyInstance(classLoader, interfaces, handler);
168 				cache.put(provider.getType(), cached);
169 				return cached;
170 			}
171 		}
172 		throw new IllegalArgumentException("Unsupported Type class " + provider.getType().getClass().getName());
173 	}
174 
175 
176 	/**
177 	 * Additional interface implemented by the type proxy.
178 	 */
179 	static interface SerializableTypeProxy {
180 
181 		/**
182 		 * Return the underlying type provider.
183 		 */
184 		TypeProvider getTypeProvider();
185 
186 	}
187 
188 
189 	/**
190 	 * A {@link Serializable} interface providing access to a {@link Type}.
191 	 */
192 	static interface TypeProvider extends Serializable {
193 
194 		/**
195 		 * Return the (possibly non {@link Serializable}) {@link Type}.
196 		 */
197 		Type getType();
198 
199 		/**
200 		 * Return the source of the type or {@code null}.
201 		 */
202 		Object getSource();
203 	}
204 
205 
206 	/**
207 	 * Default implementation of {@link TypeProvider} with a {@code null} source.
208 	 */
209 	@SuppressWarnings("serial")
210 	private static abstract class DefaultTypeProvider implements TypeProvider {
211 
212 		@Override
213 		public Object getSource() {
214 			return null;
215 		}
216 
217 	}
218 
219 
220 	/**
221 	 * {@link Serializable} {@link InvocationHandler} used by the Proxied {@link Type}.
222 	 * Provides serialization support and enhances any methods that return {@code Type}
223 	 * or {@code Type[]}.
224 	 */
225 	@SuppressWarnings("serial")
226 	private static class TypeProxyInvocationHandler implements InvocationHandler, Serializable {
227 
228 		private final TypeProvider provider;
229 
230 		public TypeProxyInvocationHandler(TypeProvider provider) {
231 			this.provider = provider;
232 		}
233 
234 		@Override
235 		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
236 			if (GET_TYPE_PROVIDER_METHOD.equals(method)) {
237 				return this.provider;
238 			}
239 			if (EQUALS_METHOD.equals(method)) {
240 				Object other = args[0];
241 				// Unwrap proxies for speed
242 				if (other instanceof Type) {
243 					other = unwrap((Type) other);
244 				}
245 				return this.provider.getType().equals(other);
246 			}
247 			if (Type.class.equals(method.getReturnType()) && args == null) {
248 				return forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, -1));
249 			}
250 			if (Type[].class.equals(method.getReturnType()) && args == null) {
251 				Type[] result = new Type[((Type[]) method.invoke(this.provider.getType(), args)).length];
252 				for (int i = 0; i < result.length; i++) {
253 					result[i] = forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, i));
254 				}
255 				return result;
256 			}
257 			try {
258 				return method.invoke(this.provider.getType(), args);
259 			}
260 			catch (InvocationTargetException ex) {
261 				throw ex.getTargetException();
262 			}
263 		}
264 	}
265 
266 
267 	/**
268 	 * {@link TypeProvider} for {@link Type}s obtained from a {@link Field}.
269 	 */
270 	@SuppressWarnings("serial")
271 	static class FieldTypeProvider implements TypeProvider {
272 
273 		private final String fieldName;
274 
275 		private final Class<?> declaringClass;
276 
277 		private transient Field field;
278 
279 		public FieldTypeProvider(Field field) {
280 			this.fieldName = field.getName();
281 			this.declaringClass = field.getDeclaringClass();
282 			this.field = field;
283 		}
284 
285 		@Override
286 		public Type getType() {
287 			return this.field.getGenericType();
288 		}
289 
290 		@Override
291 		public Object getSource() {
292 			return this.field;
293 		}
294 
295 		private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
296 			inputStream.defaultReadObject();
297 			try {
298 				this.field = this.declaringClass.getDeclaredField(this.fieldName);
299 			}
300 			catch (Throwable ex) {
301 				throw new IllegalStateException("Could not find original class structure", ex);
302 			}
303 		}
304 	}
305 
306 
307 	/**
308 	 * {@link TypeProvider} for {@link Type}s obtained from a {@link MethodParameter}.
309 	 */
310 	@SuppressWarnings("serial")
311 	static class MethodParameterTypeProvider implements TypeProvider {
312 
313 		private final String methodName;
314 
315 		private final Class<?>[] parameterTypes;
316 
317 		private final Class<?> declaringClass;
318 
319 		private final int parameterIndex;
320 
321 		private transient MethodParameter methodParameter;
322 
323 		public MethodParameterTypeProvider(MethodParameter methodParameter) {
324 			if (methodParameter.getMethod() != null) {
325 				this.methodName = methodParameter.getMethod().getName();
326 				this.parameterTypes = methodParameter.getMethod().getParameterTypes();
327 			}
328 			else {
329 				this.methodName = null;
330 				this.parameterTypes = methodParameter.getConstructor().getParameterTypes();
331 			}
332 			this.declaringClass = methodParameter.getDeclaringClass();
333 			this.parameterIndex = methodParameter.getParameterIndex();
334 			this.methodParameter = methodParameter;
335 		}
336 
337 
338 		@Override
339 		public Type getType() {
340 			return this.methodParameter.getGenericParameterType();
341 		}
342 
343 		@Override
344 		public Object getSource() {
345 			return this.methodParameter;
346 		}
347 
348 		private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
349 			inputStream.defaultReadObject();
350 			try {
351 				if (this.methodName != null) {
352 					this.methodParameter = new MethodParameter(
353 							this.declaringClass.getDeclaredMethod(this.methodName, this.parameterTypes), this.parameterIndex);
354 				}
355 				else {
356 					this.methodParameter = new MethodParameter(
357 							this.declaringClass.getDeclaredConstructor(this.parameterTypes), this.parameterIndex);
358 				}
359 			}
360 			catch (Throwable ex) {
361 				throw new IllegalStateException("Could not find original class structure", ex);
362 			}
363 		}
364 	}
365 
366 
367 	/**
368 	 * {@link TypeProvider} for {@link Type}s obtained by invoking a no-arg method.
369 	 */
370 	@SuppressWarnings("serial")
371 	static class MethodInvokeTypeProvider implements TypeProvider {
372 
373 		private final TypeProvider provider;
374 
375 		private final String methodName;
376 
377 		private final int index;
378 
379 		private transient Object result;
380 
381 		public MethodInvokeTypeProvider(TypeProvider provider, Method method, int index) {
382 			this.provider = provider;
383 			this.methodName = method.getName();
384 			this.index = index;
385 			this.result = ReflectionUtils.invokeMethod(method, provider.getType());
386 		}
387 
388 		@Override
389 		public Type getType() {
390 			if (this.result instanceof Type || this.result == null) {
391 				return (Type) this.result;
392 			}
393 			return ((Type[])this.result)[this.index];
394 		}
395 
396 		@Override
397 		public Object getSource() {
398 			return null;
399 		}
400 
401 		private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
402 			inputStream.defaultReadObject();
403 			Method method = ReflectionUtils.findMethod(this.provider.getType().getClass(), this.methodName);
404 			this.result = ReflectionUtils.invokeMethod(method, this.provider.getType());
405 		}
406 	}
407 
408 }