View Javadoc

1   /***
2    * License Agreement.
3    * 
4    * JSPA (POJO-SP)
5    * 
6    * Copyright (C) 2009 HRX Pty Ltd
7    * 
8    * This file is part of JSPA.
9    * 
10   * JSPA is free software: you can redistribute it and/or modify it under the
11   * terms of the GNU Lesser General Public License version 3 as published by the
12   * Free Software Foundation.
13   * 
14   * JSPA is distributed in the hope that it will be useful, but WITHOUT ANY
15   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16   * A PARTICULAR PURPOSE. See the Lesser General Public License for more details.
17   * 
18   * You should have received a copy of the GNU Lesser General Public License
19   * along with JSPA. If not, see <http://www.gnu.org/licenses/>.
20   */
21  package com.hrx.rasp.util.dao.metadata;
22  
23  import java.lang.annotation.Annotation;
24  import java.lang.reflect.Constructor;
25  import java.lang.reflect.Field;
26  import java.lang.reflect.InvocationTargetException;
27  import java.lang.reflect.Method;
28  import java.lang.reflect.Modifier;
29  import java.sql.ResultSet;
30  import java.sql.SQLException;
31  import java.util.Arrays;
32  
33  import org.apache.log4j.Logger;
34  
35  import com.hrx.rasp.util.dao.NullValue;
36  import com.hrx.rasp.util.dao.StoredProcUtility;
37  import com.hrx.rasp.util.dao.annotations.DAOConstructor;
38  import com.hrx.rasp.util.dao.exception.GetterNotFoundException;
39  import com.hrx.rasp.util.dao.exception.InvalidFieldException;
40  import com.hrx.rasp.util.dao.exception.InvalidGetterException;
41  import com.hrx.rasp.util.dao.exception.InvalidSetterException;
42  import com.hrx.rasp.util.dao.exception.InvalidStoredProcedureException;
43  import com.hrx.rasp.util.dao.exception.SetterNotFoundException;
44  import com.hrx.rasp.util.dao.exception.StoredProcedurePrepareException;
45  import com.hrx.rasp.util.dao.exception.StoredProcedureProccessResultException;
46  import com.hrx.rasp.util.dao.operation.OperationType;
47  
48  /***
49   * @author dan.stoica <dan.stoica@acslink.net.au>
50   * 
51   */
52  public class MetadataHelper
53  {
54  
55  	private static Logger log = Logger.getLogger(MetadataHelper.class);
56  
57  	protected static final String GET_METHOD = "get";
58  	protected static final String SET_METHOD = "set";
59  
60  	/***
61  	 * Invoke the method on a java Object.
62  	 */
63  	public static Object invokeMethod(final Method method, final Object object, final Object[] values) throws IllegalAccessException,
64  					InvocationTargetException
65  	{
66  		// we can not invoke the method if it is not accessible
67  		if (!method.isAccessible())
68  		{
69  			method.setAccessible(true);
70  		}
71  		return method.invoke(object, values);
72  	}
73  
74  	/***
75  	 * Invoke the method on a java Object with null parameters.
76  	 */
77  	public static Object invokeMethod(final Method method, final Object object) throws IllegalAccessException, InvocationTargetException
78  	{
79  		Object[] nullValues = {};
80  		return invokeMethod(method, object, nullValues);
81  	}
82  
83  	/***
84  	 * Does a recursive search for the method in the classes or it's superclass.
85  	 */
86  	public static final Method getMethod(Class<?> pojoClass, String methodName, Class<?>[] methodParameterTypes) throws NoSuchMethodException
87  	{
88  		// For an extended class we have to find the most derived method
89  		// version. We start searching for the named method with the given class
90  		// and walk up the superclass chain
91  
92  		try
93  		{
94  			return getDeclaredMethod(pojoClass, methodName, methodParameterTypes);
95  		}
96  		catch (NoSuchMethodException e)
97  		{
98  			Class<?> superclass = pojoClass.getSuperclass();
99  			if (superclass == null)
100 			{
101 				throw e;
102 			}
103 			else
104 			{
105 				return getMethod(superclass, methodName, methodParameterTypes);
106 			}
107 		}
108 	}
109 
110 	/***
111 	 * Get a method declared in the given class and suppress the Java language
112 	 * access checking when it is used.
113 	 */
114 	private static Method getDeclaredMethod(final Class<?> pojoClass, final String methodName, final Class<?>[] methodParameterTypes)
115 					throws NoSuchMethodException
116 	{
117 		Method method = pojoClass.getDeclaredMethod(methodName, methodParameterTypes);
118 		method.setAccessible(true);
119 		return method;
120 	}
121 
122 	/***
123 	 * Does a recursive search for the fields in the class or it's superclass.
124 	 */
125 	public final static Field[] getDeclaredFields(Class<?> pojoClass)
126 	{
127 		Field[] fields = pojoClass.getDeclaredFields();
128 
129 		// For an extended class we have to find the superclass fields too
130 		// so we walk up the superclass chain
131 		Class<?> superClass = pojoClass.getSuperclass();
132 		if (superClass != null)
133 		{
134 			Field[] superFields = getDeclaredFields(superClass);
135 
136 			if (superFields.length > 0)
137 			{
138 				Field[] allFields = new Field[fields.length + superFields.length];
139 				System.arraycopy(fields, 0, allFields, 0, fields.length);
140 				System.arraycopy(superFields, 0, allFields, fields.length, superFields.length);
141 				return allFields;
142 			}
143 		}
144 		return fields;
145 	}
146 
147 	/***
148 	 * Does a recursive search for the fields in the Object or it's super.
149 	 */
150 	public final static <T> Field[] getDeclaredFields(T pojo)
151 	{
152 		Class<?> clazz = pojo.getClass();
153 		Field[] fields = getDeclaredFields(clazz);
154 		return fields;
155 	}
156 
157 	protected static <T extends Annotation> StoredProcField<T> createStoredProcField(final Field field, final T parm)
158 					throws StoredProcedurePrepareException, InvalidFieldException
159 	{
160 
161 		StoredProcUtility.validateField(field);
162 
163 		String name = field.getName();
164 		try
165 		{
166 			StoredProcField<T> spField = new StoredProcField<T>(name, field, parm);
167 			if (log.isTraceEnabled())
168 			{
169 				log.trace("DAO: Found " + parm.annotationType().getSimpleName() + " parameter. Create field=" + name + ", param:" + spField);
170 			}
171 			return spField;
172 		}
173 		catch (Exception e)
174 		{
175 			throw new StoredProcedurePrepareException("Can not read the value for field:" + field.getName());
176 		}
177 	}
178 
179 	public static Object getPropertyValue(final Field field, final Object spBean) throws NoSuchMethodException, IllegalAccessException,
180 					InvocationTargetException
181 	{
182 		String methodName = GET_METHOD + StoredProcUtility.capitalize(field.getName());
183 
184 		Class<?> myClass = spBean.getClass();
185 		Method m = getGetMethod(myClass, methodName);
186 
187 		Object value = invokeMethod(m, spBean);
188 
189 		if (value == null)
190 		{
191 			value = NullValue.getFor(m.getReturnType());
192 		}
193 		return value;
194 	}
195 
196 	public static Object setPropertyValue(Field field, Object value, Object spBean) throws StoredProcedureProccessResultException
197 	{
198 		String methodName = SET_METHOD + StoredProcUtility.capitalize(field.getName());
199 
200 		try
201 		{
202 			Class<?> myClass = spBean.getClass();
203 
204 			Class<?>[] params =
205 			{
206 				field.getType()
207 			};
208 			Method m = getMethod(myClass, methodName, params);
209 			Object[] args =
210 			{
211 				value
212 			};
213 			invokeMethod(m, spBean, args);
214 
215 		}
216 		catch (Exception e)
217 		{
218 			throw new StoredProcedureProccessResultException("Failed to call '" + methodName + "' method in dao (" + spBean + ")", e);
219 		}
220 
221 		return value;
222 	}
223 
224 	public static Method getGetMethod(Object spBean, String fieldName) throws NoSuchMethodException
225 	{
226 		String methodName = GET_METHOD + StoredProcUtility.capitalize(fieldName);
227 		Class<?> myClass = spBean.getClass();
228 		Method m = getGetMethod(myClass, methodName);
229 		return m;
230 	}
231 
232 	/***
233 	 * @param methodName
234 	 * @param myClass
235 	 * @return
236 	 * @throws NoSuchMethodException
237 	 */
238 	private static Method getGetMethod(Class<?> myClass, String methodName) throws NoSuchMethodException
239 	{
240 		Class<?>[] params = {};
241 		Method m = getMethod(myClass, methodName, params);
242 		return m;
243 	}
244 
245 	/***
246 	 * Find a target getter for a property field on a given class.
247 	 * 
248 	 * @param spBean
249 	 * @param field
250 	 * @return the getter method
251 	 * @throws NoSuchMethodException
252 	 */
253 	public static Method getGetMethod(Object spBean, Field field) throws NoSuchMethodException
254 	{
255 		Method m = getGetMethod(spBean, field.getName());
256 		return m;
257 	}
258 
259 	/***
260 	 * Find a target setter for a property field with a given parameter on a
261 	 * given class.
262 	 * 
263 	 * @param spBean
264 	 * @param field
265 	 * @return the setter method
266 	 * @throws NoSuchMethodException
267 	 */
268 	public static Method getSetMethod(Object spBean, Field field) throws NoSuchMethodException
269 	{
270 		String methodName = SET_METHOD + StoredProcUtility.capitalize(field.getName());
271 		Class<?> myClass = spBean.getClass();
272 		Class<?>[] params =
273 		{
274 			field.getType()
275 		};
276 		Method m = getMethod(myClass, methodName, params);
277 		return m;
278 	}
279 
280 	protected static void validateSettter(Object spBean, Field field) throws SetterNotFoundException, InvalidSetterException
281 	{
282 		try
283 		{
284 			Method m = MetadataHelper.getSetMethod(spBean, field);
285 			int modifiers = m.getModifiers();
286 			if (!Modifier.isPublic(modifiers))
287 			{
288 				throw new InvalidSetterException("Invalid Modifiers for method :" + m.getName() + ". The setters has to be public.");
289 			}
290 		}
291 		catch (NoSuchMethodException e)
292 		{
293 			// we should support final fields.
294 			// throw new SetterNotFoundException("Setter not found for IN
295 			// parameter:" + field.getName(), e);
296 		}
297 	}
298 
299 	private static void validateGetter(Object spBean, Field field) throws GetterNotFoundException, InvalidGetterException
300 	{
301 		try
302 		{
303 			Method m = MetadataHelper.getGetMethod(spBean, field);
304 			int modifiers = m.getModifiers();
305 			if (!Modifier.isPublic(modifiers))
306 			{
307 				throw new InvalidGetterException("Invalid Modifiers for method :" + m.getName());
308 			}
309 		}
310 		catch (NoSuchMethodException e)
311 		{
312 			throw new GetterNotFoundException("Getter not found for IN parameter:'" + field.getName(), e);
313 		}
314 	}
315 
316 	public static void validateGetterAndSetter(Object spBean, Field field) throws InvalidStoredProcedureException
317 	{
318 		MetadataHelper.validateGetter(spBean, field);
319 		MetadataHelper.validateSettter(spBean, field);
320 	}
321 
322 	/***
323 	 * @param rs
324 	 * @param cons
325 	 * @param parameterTypes
326 	 * @param args
327 	 * @return
328 	 * @throws SQLException
329 	 * @throws InstantiationException
330 	 * @throws IllegalAccessException
331 	 * @throws InvocationTargetException
332 	 */
333 	public final static <T> T createInstance(ResultSet rs, Constructor<T> cons) throws SQLException, InstantiationException, IllegalAccessException,
334 					InvocationTargetException
335 	{
336 		Class<?>[] parameterTypes = cons.getParameterTypes();
337 		Object[] args = new Object[parameterTypes.length];
338 		for (int index = 0; index < parameterTypes.length; index++)
339 		{
340 			Class<?> argc = parameterTypes[index];
341 			args[index] = StoredProcUtility.getResultSetObject(rs, index + 1, argc);
342 		}
343 		T dao = cons.newInstance(args);
344 		return dao;
345 	}
346 
347 	/***
348 	 * Searches the Constructor operations array for the specified operation
349 	 * using the binary search algorithm. The array is sorted before the search.
350 	 * 
351 	 * @param operation
352 	 * @param param
353 	 * @return true if the named Operation or 'ALL' operation is contained in
354 	 *         the array
355 	 */
356 	public static boolean isOperationDefined(final OperationType operation, DAOConstructor constr)
357 	{
358 		OperationType[] ops = constr.operation();
359 		Arrays.sort(ops);
360 		if (Arrays.binarySearch(ops, operation) >= 0)
361 		{
362 			return true;
363 		}
364 		return Arrays.binarySearch(ops, OperationType.ALL) >= 0;
365 	}
366 	
367 }