1. 准备依赖包
本文示例数据库为 MySQL,持久化框架为 iBATIS,spring 版本是为 3.1.2.RELEASE,所以相关依赖包如下所示:
把这些包都考到 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 完成服务监听类的创建。
新建好的类 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>
如下图所示
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);
至此,所有准备工作就绪。整个项目结构如下图所示:
所有其他监听类通过对 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。