View Javadoc

1   package net.sf.mavenhist.persistency;
2   
3   import java.math.BigDecimal;
4   import java.sql.Connection;
5   import java.sql.DriverManager;
6   import java.sql.PreparedStatement;
7   import java.sql.ResultSet;
8   import java.sql.SQLException;
9   import java.sql.Timestamp;
10  
11  import net.sf.mavenhist.util.ParamCheckUtil;
12  
13  import org.apache.commons.lang.builder.ToStringBuilder;
14  
15  /**
16   * Supports an easy implementation of a DB-persistency Layer. It supports connection handling and
17   * execution of queries with exception handling.
18   */
19  public abstract class AbstractDatabasePersistency implements IPersistency, IConfigurablePersistency {
20  
21    private String dbUser;
22    private String dbPassword;
23    private String dbURL;
24  
25    /**
26     * Creates a new db persistency object.
27     * @param dbUser the username for the db.
28     * @param dbPassword the password for the db.
29     * @param dbURL the server URL of the db.
30     */
31    public AbstractDatabasePersistency(String dbUser, String dbPassword,
32        String dbURL) {
33      this.dbUser = dbUser;
34      this.dbPassword = dbPassword;
35      this.dbURL = dbURL;
36    }
37  
38    /**
39     * Gets a db connection, must be released by calling
40     * {@link #closeConnection(Connection)}.
41     * 
42     * @return db connection
43     * @throws PersistencyException in case of error
44     */
45    protected Connection getConnection() throws PersistencyException {
46  
47      Connection conn;
48      try {
49        Class.forName(getDbDriverClass());
50        conn = DriverManager.getConnection(getURL(), getUser(), getPassword());
51        conn.setAutoCommit(false);
52      } catch (ClassNotFoundException e) {
53        throw new PersistencyException("Could not load db driver.", e);
54      } catch (SQLException e) {
55        throw new PersistencyException("Could not get db connection.", e);
56      }
57  
58      return conn;
59    }
60  
61    /**
62     * Commits on connection and rethrows eventually generated <code>Exceptions</code>.
63     * 
64     * @param conn to commit on.
65     * @throws PersistencyException thrown when commit fails.
66     */
67    protected void commit(Connection conn) throws PersistencyException {
68      try {
69        conn.commit();
70      } catch (SQLException e) {
71        throw new PersistencyException("Could not commit.", e);
72      }
73    }
74  
75    /**
76     * Closes a db connection opened by {@link #getConnection()}.
77     * 
78     * @param conn to be closed.
79     * @throws PersistencyException in case of error
80     */
81    protected void closeConnection(Connection conn) throws PersistencyException {
82      try {
83        if (conn != null) {
84          conn.close();
85        }
86      } catch (SQLException e) {
87        throw new PersistencyException("Could not close connection.", e);
88      }
89    }
90  
91    /**
92     * Sets prepared params into a prepared Statement.
93     * 
94     * @param preparedStatement to be set params on.
95     * @param args param array.
96     * @throws PersistencyException the arguments didn't match the prepared statement.
97     * @throws SQLException thrown when the parameters do not match.
98     */
99    private void setPreparedStatementParams(PreparedStatement preparedStatement, Object[] args)
100       throws SQLException, PersistencyException {
101     if (args == null) {
102       throw new PersistencyException("Null as args is not allowed for a prepared statement");
103     }
104     int parameterCount = preparedStatement.getParameterMetaData().getParameterCount();
105     if (parameterCount != args.length) {
106       throw new PersistencyException("Number of arguments for the prepared statement are invalid. "
107           + "Is: " + args.length + " and should be: "
108           + parameterCount);
109     }
110       for (int i = 0; i < args.length; i++) {
111         Object arg = args[i];
112         if (arg instanceof String) {
113           preparedStatement.setString(i + 1, (String) arg);
114         } else if (arg instanceof java.util.Date) {
115           // convert on the fly to timestamp..
116           Timestamp sqlTimestamp = new Timestamp((((java.util.Date) arg).getTime()));
117           preparedStatement.setTimestamp(i + 1, sqlTimestamp);
118         } else if (arg instanceof Long) {
119           preparedStatement.setLong(i + 1, ((java.lang.Long) arg).longValue());
120         } else if (arg instanceof BigDecimal) {
121           preparedStatement.setBigDecimal(i + 1, (BigDecimal) arg);
122         } else {
123           // add further types if needed...
124           // for now we try object anyway as last rescue... :-)
125           preparedStatement.setObject(i + 1, arg);
126         }
127       }
128   }
129 
130   /**
131    * Executes a query operation on the database.
132    * 
133    * @param sql to be queried.
134    * @param args to be filled in for all '?'
135    * @param resultSetProcessor to process the result set.
136    * @return the result of the resultSetProcessor that was given to the method.
137    * @throws PersistencyException in case of error.
138    */
139   public Object executeQueryStatement(String sql, Object[] args,
140       IResultSetProcessor resultSetProcessor) throws PersistencyException {
141     return executeQueryStatement(getConnection(), sql, args, resultSetProcessor);
142   }
143 
144   /**
145    * Executes a query operation on the database.
146    * 
147    * @param conn to be used.
148    * @param sql to be queried.
149    * @param args to be filled in for all '?'
150    * @param resultSetProcessor to process the result set.
151    * @return the result of the resultSetProcessor that was given to the method.
152    * @throws PersistencyException in case of error.
153    */
154   protected Object executeQueryStatement(Connection conn, String sql,
155       Object[] args, IResultSetProcessor resultSetProcessor)
156       throws PersistencyException {
157 
158     PreparedStatement preparedStatement = null;
159     ResultSet rs = null;
160     try {
161       preparedStatement = conn.prepareStatement(sql);
162 
163       // set all params now...
164       setPreparedStatementParams(preparedStatement, args);
165       rs = preparedStatement.executeQuery();
166       return resultSetProcessor.processResult(rs);
167     } catch (SQLException e) {
168       throw new PersistencyException("Could not execute statement: " + sql, e);
169     } catch (PersistencyException e) {
170       throw e;
171     } catch (Exception e) {
172       throw new PersistencyException(
173           "Could not process result set for statement: " + sql, e);
174     } finally {
175       closeResultSet(rs);
176       closePreparedStatement(preparedStatement);
177     }
178   }
179 
180   /**
181    * Executes a create operation on the database.
182    * 
183    * @param sql to be queried.
184    * @return true if the first result is a {@link ResultSet} object; false if the first result is
185    * an update count or there is no result.
186    * @throws PersistencyException in case of error.
187    */
188   public boolean executeCreateStatement(String sql) throws PersistencyException {
189     return executeCreateStatement(getConnection(), sql);
190   }
191 
192   /**
193    * Executes a create operation on the database.
194    * 
195    * @param conn to be used.
196    * @param sql to be queried.
197    * @return true if the first result is a {@link ResultSet} object; false if the first result is
198    * an update count or there is no result
199    * @throws PersistencyException in case of error.
200    */
201   protected boolean executeCreateStatement(Connection conn, String sql) throws PersistencyException {
202     PreparedStatement preparedStatement = null;
203     try {
204       preparedStatement = conn.prepareStatement(sql);
205       return preparedStatement.execute();
206     } catch (SQLException e) {
207       throw new PersistencyException("Could not execute statement: " + sql, e);
208     } finally {
209       closePreparedStatement(preparedStatement);
210     }
211   }
212 
213   /**
214    * Closes the given resultset and writes the exception if a problem occured.
215    * @param rs to close.
216    */
217   private void closeResultSet(ResultSet rs) {
218     try {
219       if (rs != null) {
220         rs.close();
221       }
222     } catch (SQLException e) {
223       e.printStackTrace();
224     }
225   }
226 
227   /**
228    * Executes an update statement (queries wont work, only 
229    * insert/update calls allowed). Commits implicitely.
230    * 
231    * @param sql to be executed
232    * @param args to be filled in for all '?'
233    * @return update record count
234    * @throws PersistencyException in case of error
235    */
236   public int executeUpdateStatement(String sql, Object[] args)
237       throws PersistencyException {
238     Connection conn = null;
239     try {
240       conn = getConnection();
241       int updateCount = executeUpdateStatement(conn, sql, args);
242       commit(conn);
243       return updateCount;
244     } finally {
245       closeConnection(conn);
246     }
247   }
248 
249   /**
250    * Executes an update statement (queries won't work, only insert/update calls allowed).
251    * 
252    * @param conn connection to be used
253    * @param sql to be executed
254    * @param args to be filled in for all '?'
255    * @return update record count
256    * @throws PersistencyException in case of error
257    */
258   protected int executeUpdateStatement(Connection conn, String sql, Object[] args)
259       throws PersistencyException {
260     PreparedStatement preparedStatement = null;
261     try {
262       preparedStatement = conn.prepareStatement(sql);
263       // set all params now...
264       setPreparedStatementParams(preparedStatement, args);
265       return preparedStatement.executeUpdate();
266     } catch (SQLException e) {
267       throw new PersistencyException("Could not execute statement: " + sql, e);
268     } finally {
269       closePreparedStatement(preparedStatement);
270     }
271   }
272 
273   /**
274    * Closes the given prepared statement and prints an error if it fails.
275    * 
276    * @param preparedStatement to close.
277    */
278   private void closePreparedStatement(PreparedStatement preparedStatement) {
279     try {
280       if (preparedStatement != null) {
281         preparedStatement.close();
282       }
283     } catch (SQLException e) {
284       e.printStackTrace(); 
285     }
286   }
287 
288   /**
289    * Stores the metrics by calling the method <code>storeMetric</code>. 
290    * {@inheritDoc}
291    */
292   public void storeMetrics(Metric[] metrics) throws PersistencyException {
293     isNecessaryParamSet();
294     
295     Connection conn = null;
296     try {
297       conn = getConnection();
298       for (int i = 0; i < metrics.length; i++) {
299       	Metric metric = metrics[i];
300       	if (metric.getMetricValue() != null) {
301           storeMetric(metrics[i], conn);
302       	} else {
303           System.out.println("Skipping metric storage, no value available for metric: " + metric);
304       	}
305       }
306     } finally {
307       closeConnection(conn);
308     }
309   }
310   
311   /**
312    * Checks that the necessairy parameters are set in the {@link AbstractDatabasePersistency} that
313    * it can store values successful.
314    * Throws <code>IllegalStateException</code> when a parameter is not set.
315    *
316    * @return are all needed parameter set.
317    */
318   private boolean isNecessaryParamSet() {
319     StringBuffer messageBuffer = new StringBuffer();
320     boolean paramsOk = ParamCheckUtil.checkParam(messageBuffer, getDbDriverClass(), "DriverClass")
321       & ParamCheckUtil.checkParam(messageBuffer, getURL(), "Url")
322       & ParamCheckUtil.checkParam(messageBuffer, getUser(), "User");
323     if (!paramsOk) {
324       throw new IllegalStateException(messageBuffer.toString());
325     }
326     return true;
327   }
328   
329   /**
330    * Stores the metric by calling the method <code>storeMetric</code>.
331    *
332    * @param metric metric to store in the DB.
333    * @throws PersistencyException if storing fails.
334    */
335   public void storeMetric(Metric metric) throws PersistencyException {
336 	  storeMetrics(new Metric[] {metric});
337   }
338 
339   /**
340    * Hook implementation to store a metric value in a customized matter.
341    * 
342    * @param metric to store in the DB.
343    * @param conn to access the database and store the metric.
344    * @throws PersistencyException thrown if something fails.
345    */
346   protected abstract void storeMetric(final Metric metric, Connection conn)
347       throws PersistencyException;
348 
349   /**
350    * Gets dbPassword.
351    * @return dbPassword
352    */
353   public String getPassword() {
354     return dbPassword;
355   }
356 
357   /**
358    * Sets dbPassword.
359    * @param dbPassword dbPassword to set
360    */
361   public void setPassword(String dbPassword) {
362     this.dbPassword = dbPassword;
363   }
364 
365   /**
366    * Gets dbURL.
367    * @return dbURL
368    */
369   public String getURL() {
370     return dbURL;
371   }
372 
373   /**
374    * Sets dbURL.
375    * @param dbURL dbURL to set
376    */
377   public void setURL(String dbURL) {
378     this.dbURL = dbURL;
379   }
380 
381   /**
382    * Gets dbUser.
383    * @return dbUser
384    */
385   public String getUser() {
386     return dbUser;
387   }
388 
389   /**
390    * Sets dbUser.
391    * @param dbUser dbUser to set
392    */
393   public void setUser(String dbUser) {
394     this.dbUser = dbUser;
395   }
396 
397   /**
398    * Gets dbDriverName.
399    * @return DB-drivername
400    */
401   public abstract String getDbDriverClass();
402 
403   /**
404    * {@inheritDoc}
405    */
406   public String toString() {
407     return ToStringBuilder.reflectionToString(this);
408   }
409 }