OSGI环境中Quartz2.2.0使用数据库连接池
有任务调度需求的人对Quartz应该不会陌生,目前,Quartz的最新版本是2.2.0,该文章也是基于2.2.0版本。为了保证任务调度系统的HA,对于JobStore我采用了数据库存储的方式。系统出现故障时,系统中正在执行的任务会在数据库中保存,并在系统恢复正常后,Quartz根据misfire的处理策略对任务进行重新调度。Quartz2.2.0默认使用的数据库连接池为c3p0。在OSGI环境中,使用该连接池时,会出现连不上数据库的错误。为了解决该问题:主要有两种思路,(1)查看C3P0的源码,修改其中不适合OSGI环境的地方。(2)对Quartz2.2.0提供其它数据库连接池的支持,比如dbcp。对于第一种方法,C3P0的源码繁多,对于工期紧的项目,工作量大,可行度压力大。对于第二种方法,通过了解Quartz2.2.0的源码,发现可行性较高。
在Quartz中,首先我们看看Quartz是如何使用连接池以及如何设置默认的数据库连接池的。
在StdSchedulerFactory类的instantiate()方法中,有如下一段:
1 // Set up any DataSources
2 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3
4 String[] dsNames = cfg.getPropertyGroups(PROP_DATASOURCE_PREFIX);
5 for (int i = 0; i < dsNames.length; i++) {
6 PropertiesParser pp = new PropertiesParser(cfg.getPropertyGroup(
7 PROP_DATASOURCE_PREFIX + "." + dsNames, true));
8
9 String cpClass = pp.getStringProperty(PROP_CONNECTION_PROVIDER_CLASS, null);
10
11 // custom connectionProvider...
12 if(cpClass != null) {
13 ConnectionProvider cp = null;
14 try {
15 cp = (ConnectionProvider) loadHelper.loadClass(cpClass).newInstance();
16 } catch (Exception e) {
17 initException = new SchedulerException("ConnectionProvider class '" + cpClass
18 + "' could not be instantiated.", e);
19 throw initException;
20 }
21
22 try {
23 // remove the class name, so it isn't attempted to be set
24 pp.getUnderlyingProperties().remove(
25 PROP_CONNECTION_PROVIDER_CLASS);
26
27 setBeanProps(cp, pp.getUnderlyingProperties());
28 cp.initialize();
29 } catch (Exception e) {
30 initException = new SchedulerException("ConnectionProvider class '" + cpClass
31 + "' props could not be configured.", e);
32 throw initException;
33 }
34
35 dbMgr = DBConnectionManager.getInstance();
36 dbMgr.addConnectionProvider(dsNames, cp);
37 } else {
38 String dsJndi = pp.getStringProperty(PROP_DATASOURCE_JNDI_URL, null);
39
40 if (dsJndi != null) {
41 boolean dsAlwaysLookup = pp.getBooleanProperty(
42 PROP_DATASOURCE_JNDI_ALWAYS_LOOKUP);
43 String dsJndiInitial = pp.getStringProperty(
44 PROP_DATASOURCE_JNDI_INITIAL);
45 String dsJndiProvider = pp.getStringProperty(
46 PROP_DATASOURCE_JNDI_PROVDER);
47 String dsJndiPrincipal = pp.getStringProperty(
48 PROP_DATASOURCE_JNDI_PRINCIPAL);
49 String dsJndiCredentials = pp.getStringProperty(
50 PROP_DATASOURCE_JNDI_CREDENTIALS);
51 Properties props = null;
52 if (null != dsJndiInitial || null != dsJndiProvider
53 || null != dsJndiPrincipal || null != dsJndiCredentials) {
54 props = new Properties();
55 if (dsJndiInitial != null) {
56 props.put(PROP_DATASOURCE_JNDI_INITIAL,
57 dsJndiInitial);
58 }
59 if (dsJndiProvider != null) {
60 props.put(PROP_DATASOURCE_JNDI_PROVDER,
61 dsJndiProvider);
62 }
63 if (dsJndiPrincipal != null) {
64 props.put(PROP_DATASOURCE_JNDI_PRINCIPAL,
65 dsJndiPrincipal);
66 }
67 if (dsJndiCredentials != null) {
68 props.put(PROP_DATASOURCE_JNDI_CREDENTIALS,
69 dsJndiCredentials);
70 }
71 }
72 JNDIConnectionProvider cp = new JNDIConnectionProvider(dsJndi,
73 props, dsAlwaysLookup);
74 dbMgr = DBConnectionManager.getInstance();
75 dbMgr.addConnectionProvider(dsNames, cp);
76 } else {
77 String dsDriver = pp.getStringProperty(PoolingConnectionProvider.DB_DRIVER);
78 String dsURL = pp.getStringProperty(PoolingConnectionProvider.DB_URL);
79
80 if (dsDriver == null) {
81 initException = new SchedulerException(
82 "Driver not specified for DataSource: "
83 + dsNames);
84 throw initException;
85 }
86 if (dsURL == null) {
87 initException = new SchedulerException(
88 "DB URL not specified for DataSource: "
89 + dsNames);
90 throw initException;
91 }
92 try {
93 PoolingConnectionProvider cp = new PoolingConnectionProvider(pp.getUnderlyingProperties());
94 dbMgr = DBConnectionManager.getInstance();
95 dbMgr.addConnectionProvider(dsNames, cp);
96 } catch (SQLException sqle) {
97 initException = new SchedulerException(
98 "Could not initialize DataSource: " + dsNames,
99 sqle);
100 throw initException;
101 }
102 }
103
104 }
105
106 }
从上面这段源码可以看出,如果在quartz.properties配置文件中没有设置PROP_CONNECTION_PROVIDER_CLASS或PROP_DATASOURCE_JNDI_URL项的话,则会使用默认的连接池PoolingConnectionProvider。那PoolingConnectionProvider中使用的是什么连接池呢?我们可以看看PoolingConnectionProvider的描述和实现:
1 /**
2* <p>
3* A <code>ConnectionProvider</code> implementation that creates its own
4* pool of connections.
5* </p>
6*
7* <p>
8* This class uses C3PO (http://www.mchange.com/projects/c3p0/index.html) as
9* the underlying pool implementation.</p>
10*
11* @see DBConnectionManager
12* @see ConnectionProvider
13*
14* @author Sharada Jambula
15* @author James House
16* @author Mohammad Rezaei
17*/
18 public class PoolingConnectionProvider implements ConnectionProvider
从类描述中我们就可以看出,该类使用的是C3P0的连接池技术,也就是说Quartz2.2.0默认使用的是C3P0连接池。然后我们看到该类实现了ConnectionProvider接口,首先看看ConnectionProvider的接口定义:
1 /**
2* Implementations of this interface used by <code>DBConnectionManager</code>
3* to provide connections from various sources.
4*
5* @see DBConnectionManager
6* @see PoolingConnectionProvider
7* @see JNDIConnectionProvider
8* @see org.quartz.utils.weblogic.WeblogicConnectionProvider
9*
10* @author Mohammad Rezaei
11*/
12 public interface ConnectionProvider {
13 /*
14 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
15 *
16 * Interface.
17 *
18 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19 */
20
21 /**
22 * @return connection managed by this provider
23 * @throws SQLException
24 */
25 Connection getConnection() throws SQLException;
26
27
28 void shutdown() throws SQLException;
29
30 void initialize() throws SQLException;
31 }
View Code 从接口的描述可以知道,只需要实现该类,然后使用自定义的连接池技术就可以解决文中开头提到的问题了。
思路明确后,我们开始实现,我依照PoolingConnectionProvider的实现写了写了我自己的dbcp连接池的实现类,代码如下:
initialize方法:
1 private void initialize(
2 String dbDriver,
3 String dbURL,
4 String dbUser,
5 String dbPassword,
6 int maxConnections,
7 int maxStatementsPerConnection,
8 String dbValidationQuery,
9 boolean validateOnCheckout,
10 int idleValidationSeconds,
11 int maxIdleSeconds) throws SQLException, SchedulerException {
12 if (dbURL == null) {
13 throw new SQLException(
14 "DBPool could not be created: DB URL cannot be null");
15 }
16
17 if (dbDriver == null) {
18 throw new SQLException(
19 "DBPool '" + dbURL + "' could not be created: " +
20 "DB driver class name cannot be null!");
21 }
22
23 if (maxConnections < 0) {
24 throw new SQLException(
25 "DBPool '" + dbURL + "' could not be created: " +
26 "Max connections must be greater than zero!");
27 }
28
29
30 datasource = new BasicDataSource();
31 datasource.setDriverClassName(dbDriver);
32
33 datasource.setUrl(dbURL);
34 datasource.setUsername(dbUser);
35 datasource.setPassword(dbPassword);
36 datasource.setMaxActive(maxConnections);
37 datasource.setMinIdle(1);
38 datasource.setMaxWait(maxIdleSeconds);
39 datasource.setMaxOpenPreparedStatements(maxStatementsPerConnection);
40
41 if (dbValidationQuery != null) {
42 datasource.setValidationQuery(dbValidationQuery);
43 if(!validateOnCheckout)
44 datasource.setTestOnBorrow(true);
45 else
46 datasource.setTestOnReturn(true);
47 datasource.setTimeBetweenEvictionRunsMillis(idleValidationSeconds);
48 }
49 }
View Code
1 public Connection getConnection() throws SQLException {
2 return datasource.getConnection();
3 }
4
5 public void shutdown() throws SQLException {
6 datasource.close();
7 }
8
9 public void initialize() throws SQLException {
10 // do nothing, already initialized during constructor call
11 try {
12 this.initialize(this.driver, this.URL, this.user, this.password,
13 maxConnections, maxStatementsPerConnection, dbValidationQuery,
14 validateOnCheckout, idleValidationSeconds, maxIdleSeconds);
15 } catch (SchedulerException e) {
16 // TODO Auto-generated catch block
17 throw new SQLException(e);
18 }
19 }
View Code 然后在quartz.properties配置文件中配置org.quartz.dataSource.dbcpDS.connectionProvider.class属性,如下:
org.quartz.dataSource.dbcpDS.connectionProvider.class: com.dawning.cloudview.app.scheduleframe.service.scheduleframe.pool.DbcpConnectionProvider
启动OSGI环境,加载DBCP所需的bundle,然后测试。通过测试,发现能正常连上数据库。该方法可行。
页:
[1]