您的位置:首页 > 其它

自己的osgi收藏(比较详尽的osgi学习文档)五与hibernate的集成

2009-01-18 20:34 453 查看
l 解决和hibernate的集成
传统方式下,hibernate通过hibernate.cfg.xml中的mapping resource来加载归入sessionfactory中的管理得po,加载过程中,hibernate通过hibernate类所在的classloader加载mapping resource中配置的hbm映射文件,并通过cglib将hbm映射文件中指定的class生成proxy.
基于osgi的系统后,产生了以下两个冲突:
1. po分散到不同的工程了,同时要保持模块的封装性,就不能所有人都修改hibernate模块里的hibernate.cfg.xml文件。
2. 封装hibernate的模块的classloader无法加载到其他模块的po对象。
解决以上冲突时基于osgi使用hibernate的关键,需要解决的就是如何将其他模块的po注册到封装hibernate的模块中,及hibernate的configuration如何加载其他模块的po和映射文件。方案如下:
增加扩展点方式可以实现。之后需要的是把class加入到hibernate的configuration中,osgi中每个bundle都是独立的classloader,hibernate所在的bundle无法加载到扩展点中classname所对应的类,,但在实现扩展点的模块中创建提供扩展的类可以实现。
这是我们目前实现的方式,通过标准的Eclipse扩展点与扩展机制,我们在Hibernate插件中plugin.xml配置文件中声明下述扩展点:

<extension-point id="org.opengoss.database.domain.object" name="domainObject"/>

在模型对象插件中声明扩展,例如:
<extension point="org.opengoss.database.domain.object">
<domainObject class="org.opengoss.alarm.core.Alarm"/>
</extension>
Hibernate插件的启动中,用代码配置生成SessionFactory,代码如下:

props.put("uid", "Hibernate:SessionFactory"); public void start(BundleContext context) throws Exception {
Configuration configuration = new Configuration().configure(new File(
"./etc/org.opengoss.database.hibernate/hibernate.cfg.xml"));
Class[] domainClasses = getDomainClasses();
for (Class domainClass : domainClasses) {

configuration.addClass(domainClass);

}

sessionFactory = configuration.buildSessionFactory();
Dictionarynew Hashtable
props.put("scope", "APPLICATION");

registration = context.registerService( SessionFactory.class.getName(), sessionFactory, props);
}

private Class[] getDomainClasses() throws Exception {
List domainClasses = new ArrayList();

IExtensionPoint point = registry .getExtensionPoint(IConstants.DOMAIN_OBJECT_EXTENSION_POINT);

IExtension[] extensions = point.getExtensions();

for (IExtension extension : extensions) {
IConfigurationElement[] elements = extension .getConfigurationElements();

for (IConfigurationElement configurationElement : elements) {

Bundle bundle = pluginContext.getBundleBySymbolId(extension .getNamespaceIdentifier());

Class domainClass = bundle.loadClass(configurationElement .getAttribute("class"));

domainClasses.add(domainClass);

}

}
return domainClasses.toArray(new Class[domainClasses.size()]);
}注意:Hibernate内部的类加载机制实在无法令人满意,尽管我们在这种方式中已经加载所有的模型类对象,但Hibernate内部仍然会调用Class.forName()去试图加载。所以,我们不得不在其自描述文件(MANIFEST.MF) 中加入描述:
DynamicImport-Package: *

u 再这里通过一个基于osgi加入hibernate支持的具体事例来分析开发具体细节与流程:
(基于一个图书查询的实现,关于一些bundle的配置(hibernate,spring,mysql等)

l 准备Hibernate Bundle
在运行时hibernate需要如下的库:(本文档实例中包的具体版本根据需求导入)
asm.jar
asm-attrs.jar
cglib-2.1.3.jar
dom4j-1.6.1.jar
antlr-2.7.6.jar
这些库都可以在hibernate下载包中的lib目录里找到。为了简单起见,我们把这些库放到hibernate Bundle里,而不把每个库打成单独的bundle。
因为在执行过程中会出现了一个错误:
java.lang.NoClassDefFoundError:org/hibernate/proxy/HibernateProxy
修改getClassLoader方法,原因是加载位置不对,增加如下蓝色字体的代码:
public ClassLoader getClassLoader() {
ClassLoader t = classLoader;
if(t==null){
t=this.getClass().getClassLoader();
}
if (t == null) {
t = getDefaultClassLoader();
}
if (t == null) {
t = getClass().getClassLoader();
}
if (t == null) {
t = Thread.currentThread().getContextClassLoader();
}
if (t == null) {
throw new IllegalStateException("Cannot determine classloader");
}
return t;
}
这个文件我们单独编译放在bundle的根目录里,MANIFEST.MF文件的内容如下:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Hibernate Plug-in
Bundle-SymbolicName: hibernate
Bundle-Version: 3.2.5
Bundle-ClassPath: lib/asm.jar,
lib/asm-attrs.jar,
lib/cglib-2.1.3.jar,
lib/dom4j-1.6.1.jar,
lib/hibernate3.jar,
lib/antlr-2.7.6.jar,
.
DynamicImport-Package: *
Export-Package: org.hibernate,
org.hibernate.action,(详见在eclipse中配置基于OSGi的webx项目——加入Hibernate支持)
Import-Package: javax.transaction;version="1.1.0",
org.apache.commons.collections;version="3.2.0",
org.apache.commons.logging;version="1.0.4"

l MySQL驱动Bundle
下载mysql的驱动文件,在MENIFEST.MF文件中增加如下内容,把jar文件打包成bundle:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Driver Plug-in
Bundle-SymbolicName: com.mysql.jdbc.Driver
Bundle-Version: 1.0.0
Export-Package: com.mysql.jdbc,
com.mysql.jdbc.exceptions,
com.mysql.jdbc.integration.c3p0,
com.mysql.jdbc.integration.jboss,
com.mysql.jdbc.jdbc2.optional,
com.mysql.jdbc.log,
com.mysql.jdbc.profiler,
com.mysql.jdbc.util,
org.gjt.mm.mysql

l 修改webx Bundle
在MENIFEST.MF中增加如下的内容:
Require-Bundle: org.springframework.bundle.spring.core,
org.springframework.bundle.spring.beans,
org.springframework.bundle.spring.orm,
org.springframework.bundle.spring.tx,
org.apache.struts,
hibernate,
org.apache.log4j
Import-Package: javax.servlet,
javax.servlet.http,
javax.transaction;version="1.1.0"
其中需要依赖的spring bundle是:
spring-beans-2.5.1.jar
spring-core-2.5.1.jar
spring-orm-2.5.1.jar
spring-tx-2.5.1.jar
l 运行时需要的bundle
系统要运行时需要如下的bundle,如果在运行过程中遇到问题,可以参考下面内容:
javax.servlet_2.4.0.v200706111738
javax.servlet.jsp_2.0.0.v200706191603
org.apache.struts_1.0.0
org.apache.commons.digester_1.0.0
org.apache.log4j_1.0.0
org.apache.commons.lang_1.0.0
org.apache.commons.logging_1.0.4.v200706111724
………………………………….详见转文原址,便于调试错误。
l 增加book.hibernate Bundle
问题
默认情况下Hibernate通过hibernate.cfg.xml中的mapping resource来加载归入SessionFactory中管理的PO,在加载时Hibernate通过Hibernate类所在的classloader加载Mapping resource中配置的hbm映射文件,并通过cglib将hbm映射文件中指定的class生成proxy。
改为基于OSGi后产生冲突的地方就在于PO分散到不同的工程中去了,同时要保持模块化的封装性,就不能所有人都来修改封装Hibernate模块里的hibernate.cfg.xml;另外一个冲突点在于封装Hibernate的模块的classloader无法加载到位于其他模块的PO对象,只要解决了这两个冲突,基于OSGi使用Hibernate的问题就解决了。
解决办法
1增加扩展点
要解决这两个冲突,需要解决的就是如何将其他模块的PO注册到封装Hibernate的模块中,以及Hibernate的Configuration如何加载其他模块的PO和映射文件。
基于OSGi的可扩展性,要将其他模块的PO注册到封装Hibernate的模块中,通过扩展点方式即可实现,首先来设计此扩展点,我们注意到Hibernate的Configuration.addClass加载class的方式可达到和mapping resource完全一样的效果,扩展点中需要的仅为po的classname。
增加扩展点schema文件hibernateExtension.exsd,通过可视化编辑器增加po,类型为String,修改后的文件主要内容是:
……
<sequence minOccurs="1" maxOccurs="unbounded">
<element ref="po" minOccurs="1" maxOccurs="unbounded"/>
</sequence>
……
<element name="po">
<complexType>
<attribute name="class" type="string" use="required">
<annotation>
<documentation>
</documentation>
</annotation>
</attribute>
</complexType>
</element>
……
然后增加扩展点的定义文件plugin.xml,内容为:
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
<extension-point id="hibernateExtension" name="book.hibernate.extension" schema="schema/hibernateExtension.exsd"/>
</plugin>
2 检测扩展点,实现我们的SessionFactoryBean
我们继承Spring的LocalSessionFactoryBean类,为了监听bundle注册的改变,我们实现IRegistryChangeListener接口,如下:
public class MySessionFactory extends LocalSessionFactoryBean
implements IRegistryChangeListener
在设置注册对象时,我们获取到扩展点:
public void setRegistry(IExtensionRegistry registry) {
logger.info("************setRegistry");
registry.addRegistryChangeListener(this, "book.hibernate");
this.registry = registry;
IExtension[] extensions = registry.getExtensionPoint(
"book.hibernate.hibernateExtension").getExtensions();
for (int i = 0; i < extensions.length; i++) {
poExtensions.add(extensions[i]);
}
}
获取到扩展点的实现后,需要的就是把class加入到Hibernate的Configuration中, OSGi中每个Bundle都是独立的ClassLoader,那也就是说Hibernate所在的这个Bundle是无法加载到扩展点中className对应的类的,所幸的是IExtension的IConfigurationElement提供了初始化扩展中class的方法,也就是说可以实现在扩展点的模块中直接创建提供扩展的模块中的类,实现的方法是:
private void addHibernateConfig()
throws HibernateException {
logger.info("************addHibernateConfig");
Class poClass = null;
try {
for (Iterator iter = poExtensions.iterator(); iter.hasNext();) {
IExtension extension = (IExtension) iter.next()
IConfigurationElement[] elements = extension.getConfigurationElements()
for (int j = 0; j < elements.length; j++) {
logger.info("************elements:"+elements[j]);
poClass = elements[j].createExecutableExtension("class")
.getClass();
configuration.addClass(poClass);
}
}
} catch (Throwable t) {
String message = "Hibernate config isn't add.";
logger.error(message, t);
throw new HibernateException(message + t);
}
logger.info("************addHibernateConfig OK");
}
那么什么时候应用这些扩展点呢?我们通过重载LocalSessionFactoryBean的postProcessConfiguration方法,这个时候我们把扩展点中的类的实例加入SessionFactory的管理范围内:
protected void postProcessConfiguration(Configuration config)
throws HibernateException {
this.configuration = config;
this.addHibernateConfig();
}
上面的实现方式解决了静态时Hibernate加载其他模块中的PO初始化SessionFactory的问题,但记住要保持好基于OSGi的系统的动态性的特征,要保持动态化,就得监听Hibernate模块这个扩展点的扩展实现的变化情况,当系统中增加了新的需要归入SessionFactory管理的PO时,需要动态的加入到目前的SessionFactory中,当已有的PO从系统中删除时,也需要动态的将其从目前的SessionFactory中删除,不过Hibernate的SessionFactory是不支持动态的删除和增加其中管理的PO的,当发生变化时,只能重新初始化SessionFactory了。所以我们实现了IRegistryChangeListener中的registryChanged方法:
public void registryChanged(IRegistryChangeEvent event) {
IExtensionDelta[] deltas = event.getExtensionDeltas("book.hibernate","hibernateExtension");
if (deltas.length == 0)
return;
for (int i = 0; i < deltas.length; i++) {
switch (deltas[i].getKind()) {
case IExtensionDelta.ADDED:
poExtensions.add(deltas[i].getExtension());
break;
case IExtensionDelta.REMOVED:
poExtensions.remove(deltas[i].getExtension());
break;
default:
break;
}
}
refreshSessionFactory();
}
按照上面的步骤完成后,Hibernate会根据映射文件中的classname去实例化该Class,而不是直接使用我们通过Configuration.addClass时传递给Hibernate的Class,直接使用Class.forName的方法实例化扩展出的PO会出错误,因为Hibernate类所在的ClassLoader和扩展的类所在的ClassLoader并不同,碰到这样的情况,就得使用OSGi提供给Bundle的DynamicImport-Package了,使用此属性OSGi就可以在运行期动态的为这个Bundle获取其他Bundle Export的Package,使用这种方法的话要求PO所在的Bundle将PO的Package Export,打开Bundle的MANIFEST.MF文件,在其中加上这一行:
DynamicImport-Package: *

l 配置文件
本bundle需要如下配置文件:
hibernate.properties
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.show_sql=false
hibernate.connection.release_mode=after_transaction
jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/book
jdbc.username=root
jdbc.password=root
Spring的配置文件
<osgi:reference id="iExtensionRegistry" interface="org.eclipse.core.runtime.IExtensionRegistry"/>
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>config/jdbc.properties</value>
<value>config/hibernate.properties</value>
</list>
</property>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="initialPoolSize" value="20" />
<property name="minPoolSize" value="20" />
<property name="maxPoolSize" value="50" />
<property name="acquireIncrement" value="5" />
<property name="maxIdleTime" value="10" />
<property name="maxStatements" value="0" />
<property name="testConnectionOnCheckin" value="true" />
<property name="testConnectionOnCheckout" value="true" />
<property name="idleConnectionTestPeriod" value="30" />
</bean>
<bean id="sessionFactory" class="book.hibernate.MySessionFactory">
<property name="dataSource" ref="dataSource" />
<property name="configLocations">
<list></list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<propkey="hibernate.connection.release_mode">${hibernate.connection.release_mode}</prop></props>
</property><property name="registry" ref="iExtensionRegistry" />
</bean>
<osgi:service id="factory" ref="sessionFactory" interface="org.hibernate.SessionFactory"/>
l 结构类图

l 其它内容
此bundle里需要c3p0-0.9.1.jar,为了简单起见,我把它放到bundle里了,没有把它单独作为一个bundle,另外需要引入一些package,整个MANIFEST.MF文件如下:
Manifest-Version: 1.0
Spring-Context: config/springBeans.xml
Bundle-ManifestVersion: 2
Bundle-Name: book.hibernate Plug-in
Bundle-SymbolicName: book.hibernate;singleton:=true
Bundle-Version: 1.0.0
Export-Package: book.hibernate.dao
DynamicImport-Package: *
Bundle-ClassPath: lib/c3p0-0.9.1.jar,
.
Import-Package: com.mysql.jdbc,
com.sitechasia.webx.core.dao,
com.sitechasia.webx.core.dao.hibernate3,
com.sitechasia.webx.core.support,
javax.transaction;version="1.1.0",
org.apache.commons.logging;version="1.0.4",
org.eclipse.core.runtime,
org.hibernate,
org.hibernate.cfg,
org.osgi.framework;version="1.4.0",
org.springframework.beans.factory;version="2.5.1",
…………………………………………………
l 修改book.service bundle
加入dao、model和vo
把webx例子中的这三个源代码目录复制到book.service bundle源代码目录中,并且把hbm的两个配置文件复制到model目录中。
增加hibernate扩展
扩展的定义plugin.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
<extension point="book.hibernate.hibernateExtension">
<po class="book.model.CategoryDO"/>
</extension>
</plugin>
Spring配置文件
本bundle中需要引用book.hibernate中定义的sessionFactory,在定义的两个dao的bean里都要引用sessionFactory,并且把这两个bean发布为服务,供book.service使用
<osgi:reference id="sessionFactory" interface="org.hibernate.SessionFactory"/>

<bean id="categoryDao" class="com.sitechasia.webx.book.dao.impl.CategoryDaoImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>

<bean id="bookDao" class="com.sitechasia.webx.book.dao.impl.BookDaoImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
l 其它
此bundle需要暴露三个package:book.model、book.service和book.vo,具体的MANIFEST.MF的配置如下:
Manifest-Version: 1.0
Spring-Context: config/springBeans.xml
Bundle-ManifestVersion: 2
Bundle-Name: book.service Plug-in
Bundle-SymbolicName: book.service;singleton:=true
Bundle-Version: 1.0.0
Export-Package: book.model,
book.service,
book.vo
Require-Bundle: book.hibernate
Import-Package: com.sitechasia.webx.core.model,
com.sitechasia.webx.core.service,
com.sitechasia.webx.core.service.impl,
com.sitechasia.webx.core.support,
org.aopalliance.aop;version="1.0.0",
org.apache.commons.logging;version="1.0.4"
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐