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
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
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
164 addOutParameter(spMetaData, field, out);
165 }
166
167 if (field.isAnnotationPresent(ParametersMapping.class))
168 {
169
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
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
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 }