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.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  	 * (non-Javadoc)
81  	 * 
82  	 * @see
83  	 * com.hrx.rasp.util.dao.operation.DAOCommand#setDataSource(javax.sql.DataSource
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  	 * (non-Javadoc)
99  	 * 
100 	 * @see com.hrx.rasp.util.dao.operation.DAOCommand#execute()
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 			// get the return specific to each command
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 	 * (non-Javadoc)
151 	 * 
152 	 * @see com.hrx.rasp.util.dao.operation.DAOCommand#getStoredProcName()
153 	 */
154 	public String getStoredProcName()
155 	{
156 		return metaData.getStoredProcName();
157 	}
158 
159 	/*
160 	 * (non-Javadoc)
161 	 * 
162 	 * @see com.hrx.rasp.util.dao.operation.DAOCommand#getInParameters()
163 	 */
164 	public Set<StoredProcField<IN>> getInParameters()
165 	{
166 		return metaData.getInParameters();
167 	}
168 
169 	/*
170 	 * (non-Javadoc)
171 	 * 
172 	 * @see com.hrx.rasp.util.dao.operation.DAOCommand#getOutParameters()
173 	 */
174 	public Set<StoredProcField<OUT>> getOutParameters()
175 	{
176 		return metaData.getOutParameters();
177 	}
178 
179 	/*
180 	 * (non-Javadoc)
181 	 * 
182 	 * @see com.hrx.rasp.util.dao.operation.DAOCommand#getInOutParameters()
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 			// some app error occurred
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 	 * (non-Javadoc)
329 	 * 
330 	 * @see com.hrx.rasp.util.dao.operation.DAOCommand#getConnection()
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 }