2011年8月23日 星期二

OSGI : Eclipse Virgo(3.0RC1) , JPA + EclipseLink (Issue for transaction and jdbc driver classloader)

OSGI : Eclipse Virgo + JPA with EclipseLink(JPA2.0) or Hibernate(JPA 1.0)

上面的文章裡提到在Eclipse Virgo 上使用JPA + EcliseLink ,經過幾天測試後發現一些問題,
第一個問題是JPA Transaction commit 後沒有反應
第二個問題是jdbc driver classloader 的問題

下面是Bundle 的狀況
120 	S 	plugin-webexport-iface       1.0.0 Active
139 	S 	plugin-webdomain-page-html     1.0.0 Active
134 	S 	plugin-webdomain-service.jpa-eplk  1.0.0 Active
128 	S 	plugin-webdomain-datasource     1.0.0 Active 


Jdbc driver classloader issue
當page-html call service from service.jpa-eplk時 , 會出現Exception 如下
Caused by: org.apache.commons.dbcp.SQLNestedException: Cannot load JDBC driver class 'com.mysql.Driver'
at org.apache.commons.dbcp.BasicDataSource.createData Source(BasicDataSource.java:1146)
at org.apache.commons.dbcp.BasicDataSource.getConnect ion(BasicDataSource.java:882)
at org.springframework.jdbc.datasource.DataSourceUtil s.doGetConnection(DataSourceUtils.java:113)
at org.springframework.jdbc.datasource.DataSourceUtil s.getConnection(DataSourceUtils.java:79)
而這個Exception的來源竟然是在plugin-webdomain-page-html bundle裡面,但是我們調用的是osgi service, 並沒有直接存取JPA的部份,後來在spring forum上找到了一個文章
Thread: Question about "Cannot load JDBC driver"  , 裡面提到了的是關於
Commons DBCP 的問題,因為我的Datasource export 成OSGi Service 時是使用Commons DBCP , 在默認的狀況下,要在被使用的時候Commons DBCP 才會實例化JDBC Driver , 文中提到一段就是

" It uses the context classloader that's set at this time to load the JDBC driver. This means that if you first access your Common DBCP-powered datasource via a Web request it'll be the Web module's classloader that's set and therefore your Web bundle will need to be able to see the JDBC driver's classes. "

也就是說我們會在plugin-webdomain-page-html bundle 調用serice 實例化jdbc driver,此時在plugin-webdomain-page-html budnel裡面因為沒有import jdbc driver所以會造成問題。

解法就是在plugin-webdomain-datasource Bundle 裡面在datasource bean 實例化時加上init-method & destroy-method

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- regular spring configuration file defining the beans for this bundle. 
		The configuration of OSGi definitions is kept in a separate configuration 
		file so that this file can easily be used for integration testing outside 
		of an OSGi environment -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		  init-method="createDataSource" destroy-method="close">
		  
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://127.0.0.1:3306/test" />
		<property name="username" value="root" />
		<property name="password" value="xenogears" />

	</bean>
</beans>

這樣在log裡面在datasource bundle start 時會看到以下log
[2011-08-23 15:50:04.687] DEBUG region-dm-11                 o.s.beans.factory.support.DefaultListableBeanFactory              Creating shared instance of singleton bean 'dataSource' 
[2011-08-23 15:50:04.687] DEBUG region-dm-11                 o.s.beans.factory.support.DefaultListableBeanFactory              Creating instance of bean 'dataSource' 
[2011-08-23 15:50:04.687] DEBUG region-dm-11                 o.s.beans.factory.support.DefaultListableBeanFactory              Eagerly caching bean 'dataSource' to allow for resolving potential circular references 
[2011-08-23 15:50:04.687] DEBUG region-dm-11                 o.s.beans.factory.support.DefaultListableBeanFactory              Invoking init method  'createDataSource' on bean with name 'dataSource' 
[2011-08-23 15:50:04.921] DEBUG region-dm-11                 o.s.beans.factory.support.DefaultListableBeanFactory              Finished creating instance of bean 'dataSource' 

Transaction commit issue
第二個問題是plugin-webdomain-page-html 調用plugin-webdomain-service.jpa-eplk 時transaction 在commit 時沒有insert 資料,
下面是一個完整正常的Transaction Log
[2011-08-23 15:50:10.078] DEBUG region-dm-1                  org.springframework.orm.jpa.JpaTransactionManager                 Initiating transaction commit 
[2011-08-23 15:50:10.078] DEBUG region-dm-1                  org.springframework.orm.jpa.JpaTransactionManager                 Committing JPA transaction on EntityManager [org.eclipse.persistence.internal.jpa.EntityManagerImpl@18f67d0] 
[2011-08-23 15:50:10.078] INFO  region-dm-1                  System.out                                                        [EL Fine]: 2011-08-23 15:50:10.078--ClientSession(10164575)--Connection(10408771)--Thread(Thread[region-dm-1,5,main])--INSERT INTO bndpageinfo (id, bundle_name, bundle_version, class_name, entry_point) VALUES (?, ?, ?, ?, ?) 
[2011-08-23 15:50:10.078] INFO  region-dm-1                  System.out                                                        	bind => [139, plugin-webdomain-page-html, 1.0.0, com.gfactor.page.html.internal.NonePage, imagePage1] 
[2011-08-23 15:50:10.140] DEBUG region-dm-1                  org.springframework.orm.jpa.JpaTransactionManager                 Closing JPA EntityManager [org.eclipse.persistence.internal.jpa.EntityManagerImpl@18f67d0] after transaction 
[2011-08-23 15:50:10.140] DEBUG region-dm-1                  org.springframework.orm.jpa.EntityManagerFactoryUtils             Closing JPA EntityManager 

問題的發生在於commit結束之後insert SQL並沒有被執行, Log就會變成如下
[2011-08-23 15:50:10.078] DEBUG region-dm-1                  org.springframework.orm.jpa.JpaTransactionManager                 Initiating transaction commit 
[2011-08-23 15:50:10.078] DEBUG region-dm-1                  org.springframework.orm.jpa.JpaTransactionManager                 Committing JPA transaction on EntityManager [org.eclipse.persistence.internal.jpa.EntityManagerImpl@18f67d0] 
[2011-08-23 15:50:10.140] DEBUG region-dm-1                  org.springframework.orm.jpa.JpaTransactionManager                 Closing JPA EntityManager [org.eclipse.persistence.internal.jpa.EntityManagerImpl@18f67d0] after transaction 
[2011-08-23 15:50:10.140] DEBUG region-dm-1                  org.springframework.orm.jpa.EntityManagerFactoryUtils             Closing JPA EntityManager 

在google search一陣子之後, 大部份都是JPA Transaction 沒有驅動, 或著是在setting xml 檔的問題之類的, 比如說在Stackoverflow上這篇文章 Transaction not rolling back 就提到沒有rollback 的問題, 最主要的原因目前還沒有辦法完整的確定, 相關的討論有些是認為driver classloader 的問題, 有些可能是因為AOP Interceptor 的問題, 有的是AspectJ class proxy的問題, ok , 不論如何, 這邊碰到的是Transaction 有正常開啟, commit 也有正常執行, 無exception , 沒error , 百思不得其解的狀況下, 我做了以下幾件事情,後來就正常(這不是100%解, 也不一定在類似的問題上也可行)

1: 在上面的datasource 增加init-method & destroy-method , 讓datasource export service時instance
2: 將EclipseLink 相關的jar file 從EclipseVirgo下的repository\usr 移動到 repository\ext
3: 修改plugin-webdomain-service.jpa-eplk bundle 的dao implement class, 把
	
        @PersistenceUnit
	private EntityManagerFactory entityManagerFactory;
換成
       @PersistenceContext
        private EntityManager em;

完成上述2個之後,將Server重啟, 一切正常 , 這邊在使用JPA + EclipseLink (or other framework)時要注意的是在OSGI環境裡面除了要注意AOP classes proxy 的問題還有一些package import , export 的問題外有時還要小心datasource 的錯誤.....Eclipse Virgo上的greenpage demo 版本是2.3 , 這一個版本就沒有在DBCP 的datasource init & destrop ...

當然有人的建議是分層架構修正, 我這邊是plugin-webdomain-page-html 去呼叫 plugin-webdomain-service.jpa-eplk 的Service , 而plugin-webdomain-service.jpa-eplk 裡面defined 了TransactionManage , EntryManager....等, 而在spring forum上是有人建議bundle把transactionManager export 成osgi service , 然後在調用的service layer 使用,
Datasource(export datasource instance for osgi service)
Dao(export DAO service , TransactionManager) 
Service(reference TransactionManager from osgi service) 
這種也許也好一點


Reference:

Thread: Question about "Cannot load JDBC driver"
Transaction not rolling back
Thread: No data written using JpaTransactionManager and openJpa
Thread: JPA + @Transactional - After Rollback, SQL Querys seems to be pendent?


沒有留言:

張貼留言