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.operation;
22
23 import java.lang.reflect.Method;
24 import java.sql.CallableStatement;
25 import java.sql.Connection;
26 import java.sql.PreparedStatement;
27 import java.sql.ResultSet;
28 import java.sql.SQLException;
29 import java.util.Set;
30
31 import javax.sql.DataSource;
32
33 import org.apache.log4j.Logger;
34
35 import com.hrx.rasp.util.dao.StoredProcUtility;
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.exception.RecordNotFoundException;
40 import com.hrx.rasp.util.dao.exception.StoredProcedureException;
41 import com.hrx.rasp.util.dao.exception.StoredProcedurePrepareException;
42 import com.hrx.rasp.util.dao.exception.StoredProcedureProccessResultException;
43 import com.hrx.rasp.util.dao.exception.StoredProcedureReservedIndexException;
44 import com.hrx.rasp.util.dao.metadata.MetadataHelper;
45 import com.hrx.rasp.util.dao.metadata.StoredProcField;
46 import com.hrx.rasp.util.dao.metadata.StoredProcMetaDataImpl;
47 import com.hrx.rasp.util.dao.metadata.StoredProcPkMetaData;
48
49 /***
50 * @author dan.stoica <dan.stoica@acslink.net.au>
51 *
52 */
53 public abstract class AbstractDAOCommand implements DAOCommand
54 {
55
56 private static final String GET_UNDERLYING_CONNECTION = "getUnderlyingConnection";
57 public static final String GET_METHOD = "get";
58 public static final String SET_METHOD = "set";
59 private Logger log = Logger.getLogger(getClass());
60
61 private Object spBean;
62
63 private DataSource ds;
64 private Connection conn = null;
65 private CallableStatement st = null;
66
67 private StoredProcMetaDataImpl metaData;
68
69 private long startTime = 0;
70
71 public AbstractDAOCommand()
72 {}
73
74 public AbstractDAOCommand(DataSource ds)
75 {
76 this.ds = ds;
77 }
78
79
80
81
82
83
84
85
86 public void setDataSource(DataSource dataSource)
87 {
88 this.ds = dataSource;
89 }
90
91 protected abstract Object run() throws StoredProcedurePrepareException, StoredProcedureException, SQLException,
92 StoredProcedureProccessResultException, RecordNotFoundException;
93
94 protected abstract CallableStatement createStatement() throws StoredProcedurePrepareException, StoredProcedureReservedIndexException,
95 SQLException;
96
97
98
99
100
101
102 public final Object execute() throws StoredProcedurePrepareException, StoredProcedureException, SQLException,
103 StoredProcedureProccessResultException, RecordNotFoundException
104 {
105 try
106 {
107 this.startTime = System.currentTimeMillis();
108
109 st = createStatement();
110 if (log.isTraceEnabled())
111 {
112 double duration = StoredProcUtility.duration(startTime);
113 log.trace("DAO: SQL Statement for: '" + metaData.getStoredProcName() + "' created in: " + duration + " seconds");
114 }
115
116 long execStartTime = System.currentTimeMillis();
117 st.executeUpdate();
118
119 if (log.isInfoEnabled())
120 {
121 log.info("DAO: Execute SQL Statement for: '" + metaData.getStoredProcName() + "' in: " + StoredProcUtility.duration(execStartTime)
122 + " seconds");
123 }
124
125
126 Object result = run();
127 return result;
128 }
129 finally
130 {
131 close(st);
132 close(conn);
133
134 if (log.isDebugEnabled())
135 {
136 double duration = StoredProcUtility.duration(startTime);
137 log.debug("DAO: Stored Procedure : '" + metaData.getStoredProcName() + "' create statement, exec and parse response in: " + duration
138 + " seconds");
139 }
140 if (log.isInfoEnabled())
141 {
142 double duration = StoredProcUtility.duration(metaData.getStartTime());
143 log.info("DAO: Total Exec time for Stored Procedure (including IN/OUT parameters processing and set the result values) for: '"
144 + metaData.getStoredProcName() + "' was: " + duration + " seconds");
145 }
146 }
147 }
148
149
150
151
152
153
154 public String getStoredProcName()
155 {
156 return metaData.getStoredProcName();
157 }
158
159
160
161
162
163
164 public Set<StoredProcField<IN>> getInParameters()
165 {
166 return metaData.getInParameters();
167 }
168
169
170
171
172
173
174 public Set<StoredProcField<OUT>> getOutParameters()
175 {
176 return metaData.getOutParameters();
177 }
178
179
180
181
182
183
184 public Set<StoredProcField<INOUT>> getInOutParameters()
185 {
186 return metaData.getInOutParameters();
187 }
188
189 public Set<StoredProcField<?>> getPrimaryKeyParameters()
190 {
191 if (isEmbeddedId())
192 {
193 return metaData.getEmbeddedIdMetaData().getPrimaryKeyParameters();
194 }
195 return metaData.getPrimaryKeyParameters();
196 }
197
198 /***
199 * Check the result code from a stored proc
200 *
201 * @param storedProc
202 * the stored proc name
203 * @param resultCode
204 * its result code
205 * @param resultMsg
206 * @throws StoredProcedureException
207 * if result code indicates database error
208 * @throws SQLException
209 * if result code indicates stored proc error
210 */
211 protected void checkResultCode(String storedProc, int resultCode, String resultMsg) throws StoredProcedureException, SQLException
212 {
213 if (resultCode != 0)
214 {
215 if (log.isInfoEnabled())
216 {
217 log.info("DAO: Stored procedure " + storedProc + " exec failure. Error code: " + resultCode + " Error Message: " + resultMsg);
218 }
219
220
221 throw new StoredProcedureException(storedProc, resultCode, resultMsg);
222 }
223 if (log.isTraceEnabled())
224 {
225 log.trace("DAO: Stored procedure " + storedProc + " exec succesfull. Error Code: " + resultCode + " Result Message: " + resultMsg);
226 }
227 }
228
229 protected void checkResultCode() throws StoredProcedureException, SQLException
230 {
231 int errCodeIndex = metaData.getErrorCodeIndex();
232 int errMsgIndex = metaData.getErrorCodeIndex();
233 if (errCodeIndex > 0)
234 {
235 int errCode = st.getInt(errCodeIndex);
236 String errMsg = null;
237 if (errMsgIndex > 0)
238 {
239 errMsg = st.getString(errMsgIndex);
240 }
241 checkResultCode(getStoredProcName(), errCode, errMsg);
242 }
243 }
244
245 protected void checkResultCode(int codeIndex, int msgIndex) throws StoredProcedureException, SQLException
246 {
247 if (codeIndex > 0)
248 {
249 int errCode = st.getInt(codeIndex);
250 String errMsg = null;
251 if (codeIndex > 0)
252 {
253 errMsg = st.getString(msgIndex);
254 }
255 checkResultCode(getStoredProcName(), errCode, errMsg);
256 }
257 }
258
259 /***
260 * Closes the statement object, freeing resources. It will log, but
261 * otherwise ignore exceptions on close.
262 *
263 * @param st
264 * the statement to close
265 */
266 public void close(final PreparedStatement st)
267 {
268 try
269 {
270 if (st != null)
271 {
272 st.close();
273 }
274 }
275 catch (Throwable ex)
276 {
277 log.warn("Failed to close statement.", ex);
278 }
279 }
280
281 /***
282 * Closes the result set object, freeing resources. It will log, but
283 * otherwise ignore exceptions on close.
284 *
285 * @param _rs
286 * the statement to close
287 */
288 public void close(final ResultSet rs)
289 {
290 try
291 {
292 if (rs != null)
293 {
294 rs.close();
295 }
296 }
297 catch (Throwable ex)
298 {
299 log.warn("DAO: Failed to close result set.", ex);
300 }
301 }
302
303 /***
304 * Closes the connection object, freeing resources. If the connection is
305 * pooled it will be returned to the pool. It will log, but otherwise ignore
306 * exceptions on close.
307 */
308 public void close(Connection conn)
309 {
310 try
311 {
312 if (conn != null && !conn.isClosed())
313 {
314 conn.close();
315 if (log.isTraceEnabled())
316 {
317 log.trace("DAO: JDBC Connection CLOSED.");
318 }
319 }
320 }
321 catch (Throwable ex)
322 {
323 log.warn("Failed to close connection.", ex);
324 }
325 }
326
327
328
329
330
331
332 public Connection getConnection() throws SQLException
333 {
334 if (this.conn == null || this.conn.isClosed())
335 {
336 this.conn = ds.getConnection();
337 }
338
339 try
340 {
341 return getUnderlyingConnection();
342 }
343 catch (NoSuchMethodException e)
344 {
345 return this.conn;
346 }
347 }
348
349 /***
350 * If the connection is a JBoss Wrapper , we will retrieve the
351 * underlyingConnection. Oracle ARRAYS operation are specific just to Oracle
352 * driver. Just an workaround for Oracle.
353 *
354 * @param params
355 * @return
356 * @throws NoSuchMethodException
357 * @throws SQLException
358 */
359 private Connection getUnderlyingConnection() throws NoSuchMethodException, SQLException
360 {
361 Class<?>[] params = {};
362
363 Method underlyingConnection = MetadataHelper.getMethod(conn.getClass(), GET_UNDERLYING_CONNECTION, params);
364 try
365 {
366 return (Connection) MetadataHelper.invokeMethod(underlyingConnection, conn);
367 }
368 catch (Exception e)
369 {
370 throw new SQLException(e);
371 }
372 }
373
374 protected CallableStatement getCallableStatement()
375 {
376 return st;
377 }
378
379 public StoredProcMetaDataImpl getMetaData()
380 {
381 return metaData;
382 }
383
384 public void setMetaData(StoredProcMetaDataImpl meta)
385 {
386 this.metaData = meta;
387 }
388
389 public StoredProcPkMetaData getEmbeddedIdMetaData()
390 {
391 return metaData.getEmbeddedIdMetaData();
392 }
393
394 public Object getSpBean()
395 {
396 return spBean;
397 }
398
399 public Object getPkBean()
400 {
401 Object embeddedId = null;
402 if (this.metaData.getEmbeddedIdField() != null)
403 {
404 try
405 {
406 embeddedId = MetadataHelper.getPropertyValue(this.metaData.getEmbeddedIdField(), spBean);
407 }
408 catch (Exception e)
409 {
410 log.error("DAO: This can't happend. We should have an embeddedId. ", e);
411 }
412 }
413 return embeddedId;
414 }
415
416 public void setSpBean(Object spBean)
417 {
418 this.spBean = spBean;
419 }
420
421 /***
422 * @return the embeddedId
423 */
424 public boolean isEmbeddedId()
425 {
426 return metaData.isEmbeddedId();
427 }
428
429 }