001: /*
002: * Copyright 2001-2007 Steven Grimm <koreth[remove] at midwinter dot com>
003: * Distributed under the terms of either:
004: * - the common development and distribution license (CDDL), v1.0; or
005: * - the GNU Lesser General Public License, v2.1 or later
006: * $Id: DbConnection.java 3442 2006-08-10 09:26:43Z gbevin $
007: */
008: package com.uwyn.rife.database;
009:
010: import com.uwyn.rife.database.exceptions.DbQueryException;
011: import com.uwyn.rife.scheduler.Executor;
012: import com.uwyn.rife.scheduler.Task;
013:
014: /**
015: * Periodic probe job to keep connections non-idle and probe for dead ones.
016: * This is primarily useful for MySQL, which closes connections after a
017: * period of inactivity.
018: *
019: * <p>This should be run using a scheduler participant. For example, to
020: * probe the "mysql" Datasource once a minute:
021: *
022: * <pre> <scheduler>
023: * <task classname="com.uwyn.rife.database.DbProbeExecutor"
024: * frequency="* * * * *">
025: * <option name="datasource">mysql</option>
026: * <option name="query">select 1</option>
027: * </task>
028: * </scheduler></pre>
029: *
030: * <p>There are two optional parameters.
031: * <dl>
032: * <dt><code>datasource</code></dt>
033: * <dd>The name of the Datasource to probe. If not specified, the
034: * default is "datasource".</dd>
035: * <dt><code>query</code></td>
036: * <dd>The dummy query to send. If not specified, the default is
037: * "select 1".</dd>
038: * </dl>
039: * @author Steven Grimm (koreth[remove] at midwinter dot com)
040: * @version $Revision: $
041: * @since 1.6
042: */
043: public class DbProbeExecutor extends Executor {
044: @Override
045: public boolean executeTask(Task task) {
046: try {
047: String ds_name = task.getTaskoptionValue("datasource");
048: if (null == ds_name) {
049: ds_name = "datasource";
050: }
051:
052: String query = task.getTaskoptionValue("query");
053: if (null == query) {
054: query = "select 1";
055: }
056:
057: Datasource ds = Datasources.getRepInstance().getDatasource(
058: ds_name);
059: if (null == ds) {
060: throw new DbQueryException("Can't find Datasource '"
061: + ds_name + "'");
062: }
063:
064: ConnectionPool cp = ds.getPool();
065: if (null == cp) {
066: throw new DbQueryException("Datasource '" + ds_name
067: + "' has no ConnectionPool");
068: }
069:
070: /*
071: * Now fetch all the connections that should be in the pool,
072: * and run a dummy statement on each of them to keep it from
073: * going idle.
074: *
075: * This relies on the fact that ConnectionPool returns
076: * DbConnection objects in a round-robin fashion. We can just
077: * fetch the next connection the appropriate number of times
078: * and be guaranteed to hit all of them.
079: *
080: * If there are transactions active on other threads, we will
081: * not be given those threads' DbConnection objects, so we
082: * might end up being handed the same connection twice. No
083: * harm in that, and any connection that has an active
084: * transaction isn't idle anyway so doesn't need to be probed.
085: */
086: synchronized (cp) {
087: for (int i = 0; i < cp.getPoolsize(); i++) {
088: DbConnection conn = ds.getConnection();
089: if (null == conn) {
090: throw new DbQueryException(
091: "Can't get connection");
092: }
093:
094: DbPreparedStatement stmt = conn
095: .getPreparedStatement(query);
096: if (null == stmt) {
097: throw new DbQueryException(
098: "Can't prepare dummy statement");
099: }
100:
101: try {
102: /*
103: * Probe the connection. If this fails, RIFE will remove
104: * the connection from the pool automatically.
105: */
106: stmt.executeQuery();
107: } finally {
108: stmt.close();
109: conn.close();
110: }
111: }
112: }
113: } catch (Exception e) {
114: throw new DbQueryException("Can't probe MySQL connection",
115: e);
116: }
117:
118: return true;
119: }
120:
121: @Override
122: public String getHandledTasktype() {
123: return "MySqlProbe";
124: }
125: }
|