使用 spring 集成 dbcp 数据库连接池到 Wowza 插件

时间:2021-01-21 15:01:08
        对于 Wowza 扩展插件,很可能需要连接数据库进行一些持久化查询操作,而且也很有可能把各种业务逻辑 bean 进行集中管理起来。本文结合集成 dbcp 数据库连接池到 Wowza 插件的案例,顺带介绍 spring 和 Wowza 的集成。本文示例在 Wowza Streaming Engine 4.0.1 下执行成功。
        1. 准备依赖包
        本文示例数据库为 MySQL,持久化框架为 iBATIS,spring 版本是为 3.1.2.RELEASE,所以相关依赖包如下所示:
使用 spring 集成 dbcp 数据库连接池到 Wowza 插件
        把这些包都考到 Wowza 安装目录下的 lib 文件夹中。
        2. 新建服务监听类
        Eclipse 下新建一个 Wowza 插件项目 defonds-server-module。关于 Eclipse 安装 Wowza IDE 插件,以及插件项目的建立前面博客已有介绍,在此不再赘述。
        项目建立以后,新建 com.defonds.wms.module.server 包,然后右击该包 -> New -> Other... -> 选择 Wowza Media Server ServerListener Class -> Next -> 对话框中输入你的类名 -> 点击 Finish 完成服务监听类的创建。
使用 spring 集成 dbcp 数据库连接池到 Wowza 插件
        新建好的类 DefondsWowzaServerListener 源码如下:
package com.defonds.wms.module.server;

import com.wowza.wms.logging.*;
import com.wowza.wms.server.*;


public class DefondsWowzaServerListener implements IServerNotify2 {


	public void onServerConfigLoaded(IServer server) {
		WMSLoggerFactory.getLogger(null).info("onServerConfigLoaded");
	}


	public void onServerCreate(IServer server) {
		WMSLoggerFactory.getLogger(null).info("onServerCreate");
	}


	public void onServerInit(IServer server) {
		WMSLoggerFactory.getLogger(null).info("onServerInit");
	}


	public void onServerShutdownStart(IServer server) {
		WMSLoggerFactory.getLogger(null).info("onServerShutdownStart");
	}


	public void onServerShutdownComplete(IServer server) {
		WMSLoggerFactory.getLogger(null).info("onServerShutdownComplete");
	}


}

        3. 将新建的监听类注册到 Wowza
        使用文本编辑器打开 Wowza 安装目录下的 conf 文件夹中的 Server.xml,在 ServerListeners 标签下添加以下内容:
			<ServerListener> 
				<BaseClass>com.defonds.wms.module.server.DefondsWowzaServerListener</BaseClass> 
			</ServerListener>

        如下图所示
使用 spring 集成 dbcp 数据库连接池到 Wowza 插件
        4. 测试用数据初始化
        MySQL db 下新建数据库 rtp,然后执行以下语句:
-- ----------------------------
-- Table structure for `config`
-- ----------------------------
DROP TABLE IF EXISTS `config`;
CREATE TABLE `config` (
  `conId` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) DEFAULT NULL,
  `value` text,
  `value_type` int(11) DEFAULT '0',
  `description` varchar(256) DEFAULT NULL,
  `create_time` datetime NOT NULL,
  PRIMARY KEY (`conId`),
  UNIQUE KEY `Index_1` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;


-- ----------------------------
-- Records of config
-- ----------------------------
INSERT INTO `config` VALUES ('1', 'javax.net.ssl.truststore', '/home/cloud/lyh/relaystore.jks', '0', 'relaystore.jks文件绝对路径', '2012-11-15 16:01:47');
INSERT INTO `config` VALUES ('2', 'javax.net.ssl.truststorepassword', '123456', '0', 'relaystore.jks密码', '2012-11-15 16:01:47');

        5. 数据库配置文件的添加
        src 下新建 config.properties 并编辑其内容如下
mysql.db=172.21.0.114:3306/rtp
#mysql.db=172.21.0.114:3306/rtp
runJob=false

        6. iBATIS 支持
        新建包 com.defonds.wms.module.common.dao 并在其下新建类 BaseDao、BaseDaoImpl,新建包 com.defonds.wms.module.common.service 并在其下新建类 BaseService。BaseDao 源码如下:
package com.defonds.wms.module.common.dao;

import java.util.List;
import java.util.Map;

import org.springframework.dao.DataAccessException;
import org.springframework.orm.ibatis.SqlMapClientCallback;

import com.ibatis.sqlmap.client.event.RowHandler;

public interface BaseDao {
	public <T> T execute(SqlMapClientCallback action) throws DataAccessException;

	public <T> T  queryForObject(String statementName) throws DataAccessException;

	public <T> T  queryForObject(final String statementName, final Object parameterObject) throws DataAccessException;

	public <T> T  queryForObject(final String statementName, final Object parameterObject, final Object resultObject) throws DataAccessException;

	public List<?> queryForList(String statementName) throws DataAccessException;

	public List<?> queryForList(final String statementName, final Object parameterObject) throws DataAccessException;

	public List<?> queryForList(String statementName, int skipResults, int maxResults)throws DataAccessException;

	public List<?> queryForList(final String statementName, final Object parameterObject, final int skipResults, final int maxResults) throws DataAccessException;

	public void queryWithRowHandler(String statementName, RowHandler rowHandler)throws DataAccessException;

	public void queryWithRowHandler( final String statementName, final Object parameterObject, final RowHandler rowHandler) throws DataAccessException;

	public Map<?,?> queryForMap( final String statementName, final Object parameterObject, final String keyProperty) throws DataAccessException;

	public Map<?,?> queryForMap( final String statementName, final Object parameterObject, final String keyProperty, final String valueProperty) throws DataAccessException;

	public  <T> T insert(String statementName) throws DataAccessException;

	public <T> T insert(final String statementName, final Object parameterObject) throws DataAccessException;
	
	public int update(String statementName) throws DataAccessException;

	public int update(final String statementName, final Object parameterObject) throws DataAccessException;

	public void update(String statementName, Object parameterObject, int requiredRowsAffected) throws DataAccessException;

	public int delete(String statementName) throws DataAccessException;

	public int delete(final String statementName, final Object parameterObject) throws DataAccessException;

	public void delete(String statementName, Object parameterObject, int requiredRowsAffected) throws DataAccessException;

	public <T> void insertBatch(final String statementName, final List<T> list);

	public <T> int updateBatch(final String statementName, final List<T> list);
}

        BaseDaoImpl 源码如下:
package com.defonds.wms.module.common.dao;

import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.JdbcUpdateAffectedIncorrectNumberOfRowsException;
import org.springframework.orm.ibatis.SqlMapClientCallback;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

import com.ibatis.sqlmap.client.SqlMapExecutor;
import com.ibatis.sqlmap.client.event.RowHandler;
import com.ibatis.sqlmap.engine.execution.BatchException;
import com.ibatis.sqlmap.engine.execution.BatchResult;

public class BaseDaoImpl extends SqlMapClientDaoSupport implements BaseDao {
	public void test(){
		//SqlMapClientTemplate ddd =  super.getSqlMapClientTemplate();
	}

	private static final Log LOG = LogFactory.getLog(BaseDaoImpl.class);
	
	@SuppressWarnings("unchecked")
	public <T> T execute(SqlMapClientCallback action) throws DataAccessException {
		return (T) getSqlMapClientTemplate().execute(action);
	}


	@SuppressWarnings("unchecked")
	public <T> T  queryForObject(String statementName) throws DataAccessException {
		return (T) getSqlMapClientTemplate().queryForObject(statementName);
	}

	@SuppressWarnings("unchecked")
	public <T> T  queryForObject(final String statementName, final Object parameterObject) throws DataAccessException {
		return (T) getSqlMapClientTemplate().queryForObject(statementName, parameterObject);
	}

	@SuppressWarnings("unchecked")
	public <T> T  queryForObject(final String statementName, final Object parameterObject, final Object resultObject) throws DataAccessException {
		return (T) getSqlMapClientTemplate().queryForObject(statementName, parameterObject, resultObject);
	}

	public List<?> queryForList(String statementName) throws DataAccessException {
		return queryForList(statementName, null);
	}

	public List<?> queryForList(final String statementName, final Object parameterObject) throws DataAccessException {
		return getSqlMapClientTemplate().queryForList(statementName, parameterObject);
	}

	public List<?> queryForList(String statementName, int skipResults, int maxResults)throws DataAccessException {
		return queryForList(statementName, null, skipResults, maxResults);
	}

	public List<?> queryForList(final String statementName, final Object parameterObject, final int skipResults, final int maxResults) throws DataAccessException {
		return getSqlMapClientTemplate().queryForList(statementName, parameterObject, skipResults, maxResults);
	}

	public void queryWithRowHandler(String statementName, RowHandler rowHandler)throws DataAccessException {
		queryWithRowHandler(statementName, null, rowHandler);
	}

	public void queryWithRowHandler( final String statementName, final Object parameterObject, final RowHandler rowHandler) throws DataAccessException {
		getSqlMapClientTemplate().queryWithRowHandler(statementName, parameterObject, rowHandler);
	}

	public Map<?,?> queryForMap( final String statementName, final Object parameterObject, final String keyProperty) throws DataAccessException {
		return getSqlMapClientTemplate().queryForMap(statementName, parameterObject, keyProperty);
	}

	public Map<?,?> queryForMap( final String statementName, final Object parameterObject, final String keyProperty, final String valueProperty) throws DataAccessException {
		return getSqlMapClientTemplate().queryForMap(statementName, parameterObject, keyProperty, valueProperty);
	}

	public <T> T insert(String statementName) throws DataAccessException {
		return (T) getSqlMapClientTemplate().insert(statementName, null);
	}

	@SuppressWarnings("unchecked")
	public <T> T insert(final String statementName, final Object parameterObject) throws DataAccessException {
		return (T) getSqlMapClientTemplate().insert(statementName, parameterObject);
	}

	
	public int update(String statementName) throws DataAccessException {
		return update(statementName, null);
	}

	public int update(final String statementName, final Object parameterObject) throws DataAccessException {
		Integer result = (Integer) execute(new SqlMapClientCallback() {
			public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
				return new Integer(executor.update(statementName, parameterObject));
			}
		});
		return result.intValue();
	}

	public void update(String statementName, Object parameterObject, int requiredRowsAffected)
			throws DataAccessException {

		int actualRowsAffected = update(statementName, parameterObject);
		if (actualRowsAffected != requiredRowsAffected) {
			throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(
					statementName, requiredRowsAffected, actualRowsAffected);
		}
	}

	public int delete(String statementName) throws DataAccessException {
		return delete(statementName, null);
	}

	public int delete(final String statementName, final Object parameterObject)
			throws DataAccessException {

		Integer result = (Integer) execute(new SqlMapClientCallback() {
			public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
				return new Integer(executor.delete(statementName, parameterObject));
			}
		});
		return result.intValue();
	}

	public void delete(String statementName, Object parameterObject, int requiredRowsAffected) throws DataAccessException {

		int actualRowsAffected = delete(statementName, parameterObject);
		if (actualRowsAffected != requiredRowsAffected) {
			throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(
					statementName, requiredRowsAffected, actualRowsAffected);
		}
	}

	public <T> int updateBatch(final String statementName, final List<T> list) { 
		if(list == null || list.isEmpty()){
			return 0;
		}
		
		SqlMapClientCallback callback = new SqlMapClientCallback() {
            public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
                executor.startBatch();
                for (Object obj : list) {
                    executor.update(statementName, obj);
                }
                try {
                    return executor.executeBatchDetailed();
                } catch (BatchException e) {
                	LOG.error(e.getMessage(), e);
                    throw new SQLException("Batch update failed!");
                }
            }
        };
        List<BatchResult> retList = (List<BatchResult>) getSqlMapClientTemplate().execute(callback);
        return batchResult(retList);
	}
	
	/**
     * 从批处理结果返回处理成功的记录数
     * @param batchResultList
     * @return count
     */
    private int batchResult(List<BatchResult> batchResultList) {
        int count = 0;
        if (batchResultList != null && !batchResultList.isEmpty()) {
            BatchResult result = batchResultList.get(0);
            if (result != null) {
                for (int i : result.getUpdateCounts()) {
                    if (i == java.sql.Statement.SUCCESS_NO_INFO) {
                        count++;
                    } else {
                        count += i;
                    }
                }
            }
        }
        return count;
    }

	public <T> void insertBatch(final String statementName, final List<T> list) { 
		getSqlMapClientTemplate().execute(
				new SqlMapClientCallback() { 
		            public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException { 
		                executor.startBatch(); 
		                for (int i = 0; i < list.size(); i++) { 
		                	T t =  (T)list.get(i); 
		                    executor.insert(statementName, t); 
		                    if (i%50 == 0) {   
		                        executor.executeBatch();   
		                     }   
		                } 
		                executor.executeBatch(); 
		                return null; 
		            } 
		        }); 
		}
}

        BaseService 源码如下:
package com.defonds.wms.module.common.service;

import com.defonds.wms.module.common.dao.BaseDao;

public class BaseService {
	private BaseDao baseDao;

	public BaseDao getBaseDao() {
		return baseDao;
	}

	public void setBaseDao(BaseDao baseDao) {
		this.baseDao = baseDao;
	}
}

        然后在 src 下新建 sqlMapConfig.xml 并编辑其内容如下:
<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE sqlMapConfig      
    PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"      
    "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">

<sqlMapConfig>
	<settings cacheModelsEnabled="true"	enhancementEnabled="true" useStatementNamespaces="true"/>
	
	<sqlMap resource="config.xml"/><!-- config Related -->
	
</sqlMapConfig>

        最后在 src 下新建 config.xml 并编辑其内容如下:
<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE sqlMap      
    PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"      
    "http://ibatis.apache.org/dtd/sql-map-2.dtd">

<sqlMap namespace="config">

	<resultMap id="configData" class="com.defonds.wms.module.test.entity.Config">
	   <result property="conId" column="conId"/>
	   <result property="name" column="name" />
	   <result property="value" column="value"/>
	   <result property="valueType" column="value_type"/>
	   <result property="description" column="description"/>
	   <result property="createTime" column="create_time" />
	</resultMap>
	<select id="getAllConfigs" resultMap="configData">
		SELECT * FROM config 
	</select>

</sqlMap>

        7. 测试类的添加
        新建包 com.defonds.wms.module.test.entity 并在其下新建实体类 Config(对应于第四步的表),新建包 com.defonds.wms.module.test.service 并在其下新建测试类 TestService、TestServiceImpl。Config 源码如下:
package com.defonds.wms.module.test.entity;

import java.io.Serializable;
import java.util.Date;


/** @author Hibernate CodeGenerator */
public class Config implements Serializable {

	private static final long serialVersionUID = -5401525190715090496L;

	/** identifier field */
    private int conId;

    /** nullable persistent field */
    private String name;

    /** persistent field */
    private String value;

    /** nullable persistent field */
    private int valueType;

    /** nullable persistent field */
    private String description;

    /** persistent field */
    private Date createTime;

    public int getConId() {
        return this.conId;
    }

    public void setConId(int conId) {
        this.conId = conId;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

   
    public String getDescription() {
        return this.description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Date getCreateTime() {
        return this.createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}

	public int getValueType() {
		return valueType;
	}

	public void setValueType(int valueType) {
		this.valueType = valueType;
	}

}

        TestService 源码:
package com.defonds.wms.module.test.service;

public interface TestService {
	public void test();
}

        TestServiceImpl 源码:
package com.defonds.wms.module.test.service;

import java.util.Iterator;
import java.util.List;

import com.defonds.wms.module.test.entity.Config;
import com.defonds.wms.module.common.service.BaseService;

public class TestServiceImpl extends BaseService implements TestService {

	@Override
	public void test() {
		System.out.println("TestServiceImpl--201403251711-------");
		
		List<Config> result=(List<Config>)getBaseDao().queryForList("config.getAllConfigs");
		
		Iterator it = result.iterator();
		while (it.hasNext()) {
			Config cf = (Config) it.next();
			System.out.println(cf.getName() + ":" + cf.getValue());
		}
	}

}

        8. spring 支持
        新建包 com.defonds.wms.module.utils.spring,在包中新建类 ApplicationContextUtils 并编辑其源码如下:
package com.defonds.wms.module.utils.spring;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
 
public class ApplicationContextUtils implements ApplicationContextAware {
 
  private static ApplicationContext ctx;
 
  @Override
  public void setApplicationContext(ApplicationContext appContext)
      throws BeansException {
    ctx = appContext;
 
  }
 
  public static ApplicationContext getApplicationContext() {
    return ctx;
  }
}

        在 src 下新建 spring-context.xml 并编辑其内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/tx/spring-aop-3.1.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"
	default-autowire="byName">

	<bean
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations"
			value="classpath:config.properties" />
	</bean>

	<!-- ibatis config -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url"
			value="jdbc:mysql://${mysql.db}?useUnicode=true&characterEncoding=utf-8" />
		<property name="username" value="dev" />
		<property name="password" value="dev" />
		<property name="validationQuery" value="select 1" />
		<property name="testWhileIdle" value="true" />
		<property name="minEvictableIdleTimeMillis" value="1800000" />
	</bean>

	<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"
		scope="singleton">
		<property name="configLocation" value="classpath:sqlMapConfig.xml" />
		<property name="dataSource" ref="dataSource" />
	</bean>

	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>

	<bean id="sqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate"
		depends-on="transactionManager">
		<property name="sqlMapClient" ref="sqlMapClient" />
	</bean>

	<!-- Instruct Spring to perform declarative transaction management automatically 
		on annotated classes. -->
	<tx:annotation-driven transaction-manager="transactionManager" />

	<bean id="baseDao" class="com.defonds.wms.module.common.dao.BaseDaoImpl"
		abstract="false" lazy-init="true">
		<property name="sqlMapClientTemplate" ref="sqlMapClientTemplate" />
	</bean>

	<bean id="baseService" class="com.defonds.wms.module.common.service.BaseService"
		abstract="true" lazy-init="true">
		<property name="baseDao" ref="baseDao" />
	</bean>
	
	
	
	
	
	<!-- wowza related -->
	<bean id="applicationContextUtils"
		class="com.defonds.wms.module.utils.spring.ApplicationContextUtils"></bean>

	<bean id="strHelloWorld" class="java.lang.String">
		<constructor-arg value="201403251535Hello World" />
	</bean>
	
	<!-- test related -->
	<bean id="testService" class="com.defonds.wms.module.test.service.TestServiceImpl" parent="baseService" scope="singleton" />
	
	
	

</beans>

        然后再回到 DefondsWowzaServerListener 类,在 onServerInit 中添加代码如下:
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml"); // init here
		ApplicationContextAware aca = (ApplicationContextAware) context.getBean("applicationContextUtils"); // save this context instance to utils
		aca.setApplicationContext(context);		

        至此,所有准备工作就绪。整个项目结构如下图所示:
使用 spring 集成 dbcp 数据库连接池到 Wowza 插件
        所有其他监听类通过对 ApplicationContextUtils 的调用,即可对 spring 管理的对象进行访问了。
        测试
        在任何插件类中加入如下代码:
		/**
		 * test
		 */
		ApplicationContext appCtx = ApplicationContextUtils.getApplicationContext();


		// get any bean
		String strFromContext = (String) appCtx.getBean("strHelloWorld");
		System.out.println("DefondsWowzaServerListener-onServerInit-strFromContext=" + strFromContext);
		WMSLoggerFactory.getLogger(null).info("onServerInit");
		
		// get db data
		TestService testService = (TestService) appCtx.getBean("testService");
		testService.test();

        Eclipse 下启动 Wowza(如何启动清参阅《 使用 Wowza IDE 开发第一个 Wowza 服务器扩展应用 -- 监控直播频道 》)控制台打印结果如下:
DefondsWowzaServerListener-onServerInit-strFromContext=201403251535Hello World
INFO server comment - onServerInit
TestServiceImpl--201403251711-------
javax.net.ssl.truststore:/home/cloud/lyh/relaystore.jks
javax.net.ssl.truststorepassword:123456

        打印结果的最后两行正是我们数据库中的记录,使用 spring 集成 dbcp 数据库连接池到 Wowza 插件成功。

        注意

        如果你有多个 Wowza 插件项目,不需要每个插件都重复集成各自的 spring,部署好了一个,集成进来的 spring context 就是公用的!如果你坚持要为每个插件项目集成单独的一个 spring,请注意每个 contxt xml 配置名不要一致,否则 Wowza 只会加载其中一个,造成其他插件定义的 bean 不会初始化而造成空指针错误。

        后记
        本资源源码已上传 csdn 资源,有兴趣的朋友可以去下载一下,链接地址: http://download.csdn.net/detail/defonds/7098633