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.AnnotatedElement;
25  import java.lang.reflect.Field;
26  import java.util.Arrays;
27  
28  import javax.persistence.EmbeddedId;
29  
30  import org.apache.log4j.Logger;
31  
32  import com.hrx.rasp.util.dao.DefaultResultSetProcessor;
33  import com.hrx.rasp.util.dao.ResultSetProcessor;
34  import com.hrx.rasp.util.dao.StoredProcUtility;
35  import com.hrx.rasp.util.dao.annotations.FunctionReturn;
36  import com.hrx.rasp.util.dao.annotations.IN;
37  import com.hrx.rasp.util.dao.annotations.INOUT;
38  import com.hrx.rasp.util.dao.annotations.OUT;
39  import com.hrx.rasp.util.dao.annotations.Operation;
40  import com.hrx.rasp.util.dao.annotations.ParametersMapping;
41  import com.hrx.rasp.util.dao.annotations.ResultSetResponseProcessor;
42  import com.hrx.rasp.util.dao.exception.InvalidFieldException;
43  import com.hrx.rasp.util.dao.exception.InvalidStoredProcedureException;
44  import com.hrx.rasp.util.dao.exception.StoredProcedurePrepareException;
45  import com.hrx.rasp.util.dao.operation.OperationType;
46  
47  /***
48   * @author dan.stoica <dan.stoica@acslink.net.au>
49   * 
50   */
51  public class MetadataProcessor
52  {
53  	private static Logger log = Logger.getLogger(MetadataProcessor.class);
54  
55  	public static final String DAO_ANNOTATION_NOT_FOUND = "DAO Annotation not found.";
56  
57  	private long startTime;
58  
59  	public void process(StoredProcMetaData spMetaData, Operation operation, final Object daoBean) throws StoredProcedurePrepareException,
60  					InvalidStoredProcedureException
61  	{
62  		if (log.isDebugEnabled())
63  		{
64  			startTime = System.currentTimeMillis();
65  		}
66  
67  		Field[] fields = MetadataHelper.getDeclaredFields(daoBean);
68  
69  		for (Field field : fields)
70  		{
71  			processInOutParameters(operation, spMetaData, daoBean, field);
72  		}
73  
74  		if (log.isDebugEnabled())
75  		{
76  			double duration = StoredProcUtility.duration(startTime);
77  			log.debug("DAO: Stored Procedure '" + operation.name().toUpperCase() + "' parameters processed in  :" + duration + " seconds.");
78  		}
79  	}
80  
81  	public <T extends Annotation> void process(StoredProcMetaDataImpl spMetaData, Operation operation, final Object spBean, Class<?> returnType)
82  					throws StoredProcedurePrepareException, InvalidStoredProcedureException
83  	{
84  		process(spMetaData, operation, spBean);
85  
86  		setResultSetProcessor(spMetaData, spBean.getClass());
87  
88  		// Set the Object type returned by the select operation
89  		spMetaData.setFunctionReturnClass(returnType);
90  	}
91  
92  	/***
93  	 * Use the DefaultResultSetProcessor if no custom ResultSet processor set by
94  	 * the user
95  	 * 
96  	 * @param spMetaData
97  	 * @param returnType
98  	 */
99  	@SuppressWarnings("unchecked")
100 	private <T extends Annotation> void setResultSetProcessor(StoredProcMetaData spMetaData, Class<?> dao)
101 	{
102 		Class<?> rsPrClass;
103 		if (dao.isAnnotationPresent(ResultSetResponseProcessor.class))
104 		{
105 			ResultSetResponseProcessor rsRp = dao.getAnnotation(ResultSetResponseProcessor.class);
106 			rsPrClass = rsRp.value();
107 		}
108 		else
109 		{
110 			rsPrClass = DefaultResultSetProcessor.class;
111 		}
112 		spMetaData.setResultSetProcessorClass((Class<ResultSetProcessor<Object>>) rsPrClass);
113 	}
114 
115 	private StoredProcPkMetaData createPkMetaData(Operation operation, final Object pkBean) throws StoredProcedurePrepareException,
116 					InvalidStoredProcedureException
117 	{
118 		StoredProcPkMetaData spMetaData = new StoredProcPkMetaData();
119 		spMetaData.setOperationType(operation.type());
120 		process(spMetaData, operation, pkBean);
121 		return spMetaData;
122 	}
123 
124 	/***
125 	 * @param operation
126 	 * @param spMetaData
127 	 * @param spBean
128 	 * @param field
129 	 * @throws StoredProcedurePrepareException
130 	 * @throws InvalidStoredProcedureException
131 	 * @throws InvalidFieldException
132 	 */
133 	private <T extends Annotation> void processInOutParameters(Operation operation, StoredProcMetaData spMetaData, final Object spBean, Field field)
134 					throws StoredProcedurePrepareException, InvalidStoredProcedureException, InvalidFieldException
135 	{
136 
137 		// read parameter definitions for all Stored Procs
138 		if (field.isAnnotationPresent(IN.class))
139 		{
140 			IN in = field.getAnnotation(IN.class);
141 			addInParameter(spMetaData, field, in);
142 		}
143 
144 		if (field.isAnnotationPresent(OUT.class))
145 		{
146 			MetadataHelper.validateGetterAndSetter(spBean, field);
147 			OUT out = field.getAnnotation(OUT.class);
148 			addOutParameter(spMetaData, field, out);
149 		}
150 
151 		if (field.isAnnotationPresent(INOUT.class))
152 		{
153 			MetadataHelper.validateGetterAndSetter(spBean, field);
154 			INOUT inOut = field.getAnnotation(INOUT.class);
155 			addInOutParameter(spMetaData, field, inOut);
156 		}
157 
158 		if (field.isAnnotationPresent(FunctionReturn.class))
159 		{
160 			MetadataHelper.validateGetterAndSetter(spBean, field);
161 			FunctionReturn ret = field.getAnnotation(FunctionReturn.class);
162 			OUT out = convertToOUT(ret);
163 			// a function return is just an regular OUT parameter
164 			addOutParameter(spMetaData, field, out);
165 		}
166 
167 		if (field.isAnnotationPresent(ParametersMapping.class))
168 		{
169 			// read parameter definitions for all Stored Procs
170 			ParametersMapping params = field.getAnnotation(ParametersMapping.class);
171 
172 			for (IN in : params.in())
173 			{
174 				addInParameter(spMetaData, field, in);
175 			}
176 			for (OUT in : params.out())
177 			{
178 				addOutParameter(spMetaData, field, in);
179 			}
180 			for (INOUT in : params.inout())
181 			{
182 				addInOutParameter(spMetaData, field, in);
183 			}
184 			for (FunctionReturn ret : params.functionReturn())
185 			{
186 				OUT out = convertToOUT(ret);
187 				// a function return is just an regular OUT parameter
188 				addOutParameter(spMetaData, field, out);
189 			}
190 		}
191 
192 		if (field.isAnnotationPresent(EmbeddedId.class))
193 		{
194 			try
195 			{
196 				Object embeddedId = MetadataHelper.getPropertyValue((Field) field, spBean);
197 				if (embeddedId == null)
198 				{
199 					Class<?> pkClass = ((Field) field).getType();
200 					embeddedId = pkClass.newInstance();
201 					MetadataHelper.setPropertyValue(field, embeddedId, spBean);
202 				}
203 				StoredProcPkMetaData embeddedIdMetaData = createPkMetaData(operation, embeddedId);
204 
205 				((StoredProcMetaDataImpl) spMetaData).setEmbeddedId((Field) field, embeddedIdMetaData);
206 			}
207 			catch (Exception e)
208 			{
209 				throw new InvalidFieldException("Error setting EmbeddedId", e);
210 			}
211 		}
212 	}
213 
214 	private void addInParameter(StoredProcMetaData spMetaData, AnnotatedElement field, IN param) throws StoredProcedurePrepareException,
215 					InvalidFieldException
216 	{
217 		if (isOperationDefined(spMetaData.getOperationType(), param.operation()))
218 		{
219 			StoredProcField<IN> inField = MetadataHelper.createStoredProcField((Field) field, param);
220 			spMetaData.putInParameter(inField);
221 		}
222 	}
223 
224 	private void addOutParameter(StoredProcMetaData spMetaData, AnnotatedElement field, OUT out) throws StoredProcedurePrepareException,
225 					InvalidFieldException
226 	{
227 		if (isOperationDefined(spMetaData.getOperationType(), out.operation()))
228 		{
229 			StoredProcField<OUT> outField = createStoredProcField(out, field);
230 			spMetaData.putOutParameter(outField);
231 		}
232 	}
233 
234 	private void addInOutParameter(StoredProcMetaData spMetaData, AnnotatedElement field, INOUT inOut) throws StoredProcedurePrepareException,
235 					InvalidFieldException
236 	{
237 		if (isOperationDefined(spMetaData.getOperationType(), inOut.operation()))
238 		{
239 			StoredProcField<INOUT> inOutField = createStoredProcField(inOut, field);
240 			spMetaData.putInOutParameter(inOutField);
241 		}
242 	}
243 
244 	private <T extends Annotation> StoredProcField<T> createStoredProcField(T param, AnnotatedElement field) throws StoredProcedurePrepareException,
245 					InvalidFieldException
246 	{
247 		StoredProcField<T> spField = MetadataHelper.createStoredProcField((Field) field, param);
248 		return spField;
249 	}
250 
251 	/***
252 	 * Searches the Field Operations array for the specified Operation using the
253 	 * binary search algorithm. The array is sorted before the search.
254 	 * 
255 	 * @param operation
256 	 * @param param
257 	 * @return true if the named Operation or 'ALL' operation is contained in
258 	 *         the array
259 	 */
260 	private boolean isOperationDefined(final OperationType operation, OperationType[] ops)
261 	{
262 		Arrays.sort(ops);
263 		if (Arrays.binarySearch(ops, operation) >= 0)
264 		{
265 			return true;
266 		}
267 		return Arrays.binarySearch(ops, OperationType.ALL) >= 0;
268 	}
269 
270 	private OUT convertToOUT(final FunctionReturn ret)
271 	{
272 		if (log.isTraceEnabled())
273 		{
274 			log.trace("DAO: Convert FunctionReturn parameter:" + ret + " to OUT parameter index 1.");
275 		}
276 		OUT out = new OUT() {
277 			public Class<?> collectionType()
278 			{
279 				return ret.collectionType();
280 			}
281 
282 			public int index()
283 			{
284 				// this is a function
285 				return 1;
286 			}
287 
288 			public OperationType[] operation()
289 			{
290 				return ret.operation();
291 			}
292 
293 			public Class<?> resultSetProcessor()
294 			{
295 				return ret.resultSetProcessor();
296 			}
297 
298 			public String sqlArrayName()
299 			{
300 				return ret.sqlArrayName();
301 			}
302 
303 			public int sqlType()
304 			{
305 				return ret.sqlType();
306 			}
307 
308 			public Class<? extends Annotation> annotationType()
309 			{
310 				return ret.annotationType();
311 			}
312 
313 		};
314 		return out;
315 	}
316 
317 }