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?


2011年8月18日 星期四

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

本篇主要是針對Eclipse Virgo 3.0.0-RC1 版本上使用JPA 做設定, Virgo 3.0.0 RC1 預設jar 檔是只支援JPA 1.0 ,而Hibernate 官網的jar 檔是不包含OSGI 版本的,所以在這邊針對Hibernate 只有使用在SpringSource EBR 上找到的osgi bundle版本,JPA 2.0是使用EclipseLink 2.3。


JPA 1.0 + Hibernate
首先在SpringSource EBR上可以找到以下的jar 檔,

org.hibernate.ejb-library-3.3.1.ga.libd
com.springsource.org.hibernate.annotations-3.4.0.GA-A.jar
com.springsource.org.hibernate.annotations.common-3.3.0.ga.jar
com.springsource.org.hibernate.ejb-3.4.0.GA-A.jar
com.springsource.org.hibernate-3.3.2.GA.jar

這邊要注意版本的問題,org.hibernate.ejb-library-3.3.1.ga.libd裡面import的 bundle version可能跟下載的版本有些差異,如果碰到exception 可能要手動修改bundle裡面 Import-Package,Require-bundle 的版本資訊,這邊我有手動修改。 然後下面的是上面所需要的其他bundle

com.springsource.antlr-2.7.7.jar
com.springsource.org.apache.commons.collections-3.2.0.jarz
com.springsource.org.apache.commons.logging-1.1.1.jar
com.springsource.org.dom4j-1.6.1.jar
com.springsource.org.jgroups-2.5.1.jar
com.springsource.javassist-3.9.0.GA.jar
com.springsource.org.objectweb.asm-1.5.3.jar
com.springsource.net.sf.ehcache-1.5.0.jar
com.springsource.net.sf.jsr107cache-1.0.0.jar

然後將這些放到$EclipseVirgoHome/repository/usr 下,再來就能直接編寫測試用的bundle


先看看project結構


com.gfactor.service.iface 定義了service 的interface ,
com.gfactor.service.internal.dao 定義了dao interface &amp; implements
com.gfactor.service.jpa 定義jpa 的pojo object
下面列出來的的檔案相同也用在JPA 2.0 + EclipseLink 的例子上,

Bndpageinfo entity

package com.gfactor.service.jpa;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="bndpageinfo")
public class Bndpageinfo implements Serializable{
	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
	private long id;
	
	@Column(name="bundle_name")
	private String bundle_name;
	
	@Column(name="bundle_version")
	private String bundle_version;
	
	@Column(name="entry_point")
	private String entry_point;
	
	@Column(name="class_name")
	private String class_name;

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	



	public String getBundle_name() {
		return bundle_name;
	}

	public void setBundle_name(String bundle_name) {
		this.bundle_name = bundle_name;
	}

	public String getBundle_version() {
		return bundle_version;
	}

	public void setBundle_version(String bundle_version) {
		this.bundle_version = bundle_version;
	}

	public String getEntry_point() {
		return entry_point;
	}

	public void setEntry_point(String entry_point) {
		this.entry_point = entry_point;
	}

	public String getClass_name() {
		return class_name;
	}

	public void setClass_name(String class_name) {
		this.class_name = class_name;
	}
	
	
	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append("print Bndpageinfo object : \n");
		sb.append("id               : "  id   "\n");
		sb.append("bnudle_name      : "  bundle_name   "\n");
		sb.append("bundle_version   : "  bundle_version   "\n");
		sb.append("entry_point      : "  entry_point   "\n");
		sb.append("class_name       : "  class_name   "\n");
		return sb.toString();
	}
}

Providing the JPA metadata


<persistence xmlns="http://java.sun.com/xml/ns/persistence"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
	version="1.0">

	<persistence-unit name="plugin-web-domain"
		transaction-type="RESOURCE_LOCAL">

		<provider>org.hibernate.ejb.HibernatePersistence</provider>
		<class> com.gfactor.service.jpa.Bndpageinfo</class>
		<properties>
			<property name="hibernate.show_sql" value="true" />
			<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
			<property name="hibernate.jdbc.batch_size" value="100" />
		</properties>
	</persistence-unit>

</persistence>


Service Layer 


IRegisterWicketPageBndIdentify.java
package com.gfactor.service.iface;

import com.gfactor.service.jpa.Bndpageinfo;

public interface IRegisterWicketPageBndIdentify {
	public boolean registerPageInfo(Bndpageinfo bnd);
	public boolean unregisterPageInfo(Bndpageinfo bnd);
}


RegisterWicketPageBndIdentifyImpl.java
package com.gfactor.service.iface.internal.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.gfactor.service.iface.IRegisterWicketPageBndIdentify;
import com.gfactor.service.internal.dao.RegWicketPageBndDao;
import com.gfactor.service.jpa.Bndpageinfo;

/**
 * @author momo
 *
 */
public class RegisterWicketPageBndIdentifyImpl implements
		IRegisterWicketPageBndIdentify {
	
	@Autowired 
	private RegWicketPageBndDao regWicketPageBndDao;
	
	/* (non-Javadoc)
	 * @see com.gfactor.service.iface.IRegisterWicketPageBndIdentify#registerPageInfo()
	 */
	
	@Transactional
	public boolean registerPageInfo(Bndpageinfo bnd) {
		System.out.println("registerPageInfo , bnd = " bnd);
		 
		Bndpageinfo bndObj = regWicketPageBndDao.getBndPageInfo(bnd.getBundle_name(), bnd.getBundle_version(), bnd.getEntry_point());
		System.out.println("registerPageInfo , bndObj = " bndObj);
//		List<Bndpageinfo> findObj = regWicketPageBndDao.findBndPageInfoList(bnd.getBundle_name(), bnd.getBundle_version(), bnd.getEntry_point());
//		System.out.println("registerPageInfo , check findObj size = " findObj.size());
		 
		if(bndObj==null){
			System.out.println("register page info.......");
			regWicketPageBndDao.saveBndPageInfo(bnd);			
			return true;
		}else{
			System.out.println("page info already exists..........");
			return false;
		}

		
		
	}

	/* (non-Javadoc)
	 * @see com.gfactor.service.iface.IRegisterWicketPageBndIdentify#unregisterPageInfo()
	 */
	@Transactional
	public boolean unregisterPageInfo(Bndpageinfo bnd) {
		Bndpageinfo bndObj = regWicketPageBndDao.getBndPageInfo(bnd.getBundle_name(), bnd.getBundle_version(), bnd.getEntry_point());
//		List<Bndpageinfo> findObj = regWicketPageBndDao.findBndPageInfoList(bnd.getBundle_name(), bnd.getBundle_version(), bnd.getEntry_point());
//		System.out.println("unregisterPageInfo , check findObj size = " findObj.size());
		if(bndObj!=null){
			System.out.println("unregister page info....");
			regWicketPageBndDao.delete(bndObj);
			return true;
		}else{
			System.out.println("no page info delete , object doesn't exists.");
			return false;
		}
		
		
	}

}


DAO

RegWicketPageBndDao.java
package com.gfactor.service.internal.dao;

import java.util.List;

import com.gfactor.service.jpa.Bndpageinfo;


public interface RegWicketPageBndDao {
	public List findUser(String bndName,String bndVer,String entry_point);
	public void saveUser(Bndpageinfo bndpageinfo);
	public Bndpageinfo update(Bndpageinfo bndpageinfo);
	public void delete(Bndpageinfo bndpageinfo);
}


RegWicketPageBndDaoImpl.java
package com.gfactor.service.internal.dao.impl;

import java.util.List;

import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;

import org.springframework.orm.jpa.support.JpaDaoSupport;
import org.springframework.transaction.annotation.Transactional;

import com.gfactor.service.internal.dao.RegWicketPageBndDao;
import com.gfactor.service.jpa.Bndpageinfo;


/**
 * @author momo
 *
 */
public class RegWicketPageBndDaoImpl extends JpaDaoSupport implements RegWicketPageBndDao {
	
	@PersistenceUnit
	private EntityManagerFactory entityManagerFactory;
	
//	/**
//     * Constructor
//     */
//    @Autowired
//    public RegWicketPageBndDaoImpl(EntityManagerFactory entityManagerFactory) {
//    	System.out.println("to set entityManagerFactory ="+ entityManagerFactory);
//        super.setEntityManagerFactory(entityManagerFactory);
//    }
//    
    /**
     * get Bndpageinfo object by bndname,bndver and entry_point
     */
    
	public List findUser(String bndName, String bndVer,
			String entry_point) {
		return getJpaTemplate()
				.find("select r from Bndpageinfo r where r.bundle_name = ?1 and r.bundle_version =?2 and r.entry_point = ?3",
						bndName, bndVer, entry_point);
	}
	
	/* (non-Javadoc)
	 * @see com.gfactor.service.internal.dao.RegWicketPageBndDao#saveUser(com.gfactor.service.internal.jpa.Bndpageinfo)
	 */
	
	public void saveUser(Bndpageinfo bndpageinfo) {
		System.out.println("save user " + bndpageinfo);
		System.out.println("to get getEntityManager =" + getJpaTemplate().getEntityManager());
		System.out.println("to get getEntityManagerFactory =" + getJpaTemplate().getEntityManagerFactory());
		getJpaTemplate().persist(bndpageinfo);
		System.out.println("to get getEntityManager =" + getJpaTemplate().getEntityManager());
		
	}

	/* (non-Javadoc)
	 * @see com.gfactor.service.internal.dao.RegWicketPageBndDao#update(com.gfactor.service.internal.jpa.Bndpageinfo)
	 */
	
	public Bndpageinfo update(Bndpageinfo bndpageinfo) {
		return getJpaTemplate().merge(bndpageinfo);
	}

	/* (non-Javadoc)
	 * @see com.gfactor.service.internal.dao.RegWicketPageBndDao#delete(com.gfactor.service.internal.jpa.Bndpageinfo)
	 */
	
	public void delete(Bndpageinfo bndpageinfo) {
		
		getJpaTemplate().remove(bndpageinfo);

	}
}


再來是Spring的設定, 因為這邊是deploy在osgi container上, 所以datasource 來源也是osgi service , 下面是bundle-context-osgi.xml &amp; bundle-context.xml的設定
bundle-context-osgi.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:osgi="http://www.springframework.org/schema/osgi"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                      http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd">

  <!-- definitions using elements of the osgi namespace can be included
       in this file. There is no requirement to keep these definitions
       in a separate file if you do not want to. The rationale for 
       keeping these definitions separate is to facilitate integration
       testing of the bundle outside of an OSGi container -->
       
        <!-- import the DataSource from OSGi -->
    <osgi:reference id="dataSource" interface="javax.sql.DataSource" />
</beans>

bundle-context.xml for hibernate jpa 1.0
<?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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="
			http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
			http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
			http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.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 -->
	<!-- <context:load-time-weaver weaver-class="org.eclipse.equinox.weaving.springweaver.EquinoxAspectsLoadTimeWeaver" 
		/> -->
	<context:load-time-weaver aspectj-weaving="on" />
	<context:annotation-config />
	<!-- -->
	<aop:aspectj-autoproxy />
	<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
	<!-- <tx:annotation-driven mode="proxy" proxy-target-class="true"></tx:annotation-driven> -->
	<bean
		class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
	<bean
		class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>

	
	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="persistenceUnitName" value="plugin-web-domain"></property>
		<property name="dataSource" ref="dataSource"></property>	
	  <property name="jpaVendorAdapter"> 
	  	<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
			<property name="showSql" value="true" /> 
			<property name="generateDdl" value="true"/> 
			<property name="databasePlatform" value="org.hibernate.dialect.MySQLInnoDBDialect" />
	  </property>
	</bean>

	<!-- <bean id="queryUserDao" class="com.dao.impl.QueryUserDaoImpl" > <property 
		name="entityManagerFactory" ref="entityManagerFactory"/> </bean> -->



	<!-- defined dao bean start -->
	<bean id="regWicketPageBndDao"
		class="com.gfactor.service.internal.dao.impl.RegWicketPageBndDaoImpl">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>

	<bean id="registerWicketPageBndIdentify"
		class="com.gfactor.service.iface.internal.impl.RegisterWicketPageBndIdentifyImpl">

	</bean>

	<!-- activator bean for register page info to database at bundle start time. -->
	<bean id="bndActivator" class="com.gfactor.service.BndActivator"
		init-method="start" destroy-method="stop"></bean>

</beans>

MANIFEST.mf檔透過spring bundlor plugin可以生成,缺少的class 再手動補上就行,如果這邊想要JPA 2.0 + Hibernate 3.5↑的話就比較麻煩了一點,因為hibernate 給的jar檔是不支援osgi的,所以必須自己build一版出來,有需要的話可以參考下面的連結,
Creating custom Hibernate OSGi bundles for JPA 2.0

再來就要設定最麻煩的JPA 2.0 + EclipseLink 2.3, 下面的設定是目前的版本, 除了spring的設定 跟persistence.xml 的不同外,就是manifest.mf檔的improt-package要很完整,不然會跳一堆奇奇怪怪的exception出來....

JPA 2.0 + EclipseLink 2.3 Setting

先到EclipseLink 官網下載檔案,要選擇OSGI Bundle 版本的, Latest Release Downloads
下面的檔案放到$EclipseVirgoHome/repository/usr 下面
commonj.sdo_2.1.1.v200905221342.jar
  eclipselink-jpa-modelgen_2.3.0.v20110604-r9504.jar
  javax.persistence_2.0.3.v201010191057.jar
  javax.resource_1.5.0.jar
  javax.xml.bind_2.2.0.v201005080402.jar
  javax.xml.ws_2.0.0.v200902170419.jar
  org.eclipse.persistence.antlr_2.3.0.v20110604-r9504.jar
  org.eclipse.persistence.asm_2.3.0.v20110604-r9504.jar
  org.eclipse.persistence.core_2.3.0.v20110604-r9504.jar
  org.eclipse.persistence.dbws.builder_2.3.0.v20110604-r9504.jar
  org.eclipse.persistence.dbws_2.3.0.v20110604-r9504.jar
  org.eclipse.persistence.jpa.equinox.weaving_2.3.0.v20110604-r9504.jar
  org.eclipse.persistence.jpa.equinox_2.3.0.v20110604-r9504.jar
  org.eclipse.persistence.jpa.jpql_1.0.0.v20110604-r9504.jar
  org.eclipse.persistence.jpa.modelgen_2.3.0.v20110604-r9504.jar
  org.eclipse.persistence.jpa.osgi_2.3.0.v20110604-r9504.jar
  org.eclipse.persistence.jpa_2.3.0.v20110604-r9504.jar
  org.eclipse.persistence.moxy_2.3.0.v20110604-r9504.jar
  org.eclipse.persistence.oracle_2.3.0.v20110604-r9504.jar
  org.eclipse.persistence.sdo_2.3.0.v20110604-r9504.jar

然後這邊是已經有的(因為我的server用很久,不確定是否後來自己多加的,記的自我確認)
javax.activation_1.1.0.v201005080500.jar
        javax.jms_1.1.0.jar
        javax.mail_1.4.0.v201005080615.jar
        javax.persistence_1.0.0.v200905011740.jar
        javax.servlet_2.4.0.v200806031604.jar
        javax.transaction_1.1.0.v201002051055.jar
        javax.xml.soap_1.3.0.jar
        javax.xml.stream_1.0.1.v201004272200.jar


 

Providing the JPA metadata for EclipseLink

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
	version="2.0">

	<persistence-unit name="plugin-web-domain"
		transaction-type="RESOURCE_LOCAL">
		<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
		<!-- 
		<provider>org.hibernate.ejb.HibernatePersistence</provider>
		 -->
		<class> com.gfactor.service.jpa.Bndpageinfo</class>
		
		<properties>
			 <property name="eclipselink.weaving" value="true" />
		</properties>
		<!-- 
		<properties>
			<property name="hibernate.show_sql" value="true" />
			<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
			<property name="hibernate.jdbc.batch_size" value="100" />
		</properties>
		 -->
	</persistence-unit>

</persistence>


bundle-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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="
			http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
			http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
			http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.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 -->
	<!-- <context:load-time-weaver weaver-class="org.eclipse.equinox.weaving.springweaver.EquinoxAspectsLoadTimeWeaver" 
		/> -->
	<context:load-time-weaver aspectj-weaving="on" />
	<context:annotation-config />
	<!-- -->
	<aop:aspectj-autoproxy />
	<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
	<!-- <tx:annotation-driven mode="proxy" proxy-target-class="true"></tx:annotation-driven> -->
	<bean
		class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
	<bean
		class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>
	<!-- <property name="persistenceUnitName" value="SM" /> -->
	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="persistenceUnitName" value="plugin-web-domain"></property>
		<property name="dataSource" ref="dataSource"></property>
		<property name="jpaVendorAdapter">
			<bean id="jpaVendorAdapter"
				class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter"
				p:databasePlatform="org.eclipse.persistence.platform.database.MySQLPlatform"
				p:showSql="true" />
		</property>
		<property name="loadTimeWeaver">
			<bean
				class="org.springframework.instrument.classloading.SimpleLoadTimeWeaver " />
		</property>
		<!-- <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
			<property name="showSql" value="true" /> <property name="generateDdl" value="true" 
			/> <property name="databasePlatform" value="org.hibernate.dialect.MySQLInnoDBDialect" 
			/> </bean> </property> -->
	</bean>


	<!-- <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" 
		p:dataSource-ref="dataSource" p:persistenceUnitName="plugin-web-domain"> 
		<property name="jpaVendorAdapter"> <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter" 
		p:databasePlatform="org.eclipse.persistence.platform.database.MySQLPlatform" 
		p:showSql="true" /> </property> </bean> -->




	<!-- <bean id="queryUserDao" class="com.dao.impl.QueryUserDaoImpl" > <property 
		name="entityManagerFactory" ref="entityManagerFactory"/> </bean> -->



	<!-- defined dao bean start -->
	<bean id="regWicketPageBndDao"
		class="com.gfactor.service.internal.dao.impl.RegWicketPageBndDaoImpl">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>

	<bean id="registerWicketPageBndIdentify"
		class="com.gfactor.service.iface.internal.impl.RegisterWicketPageBndIdentifyImpl">

	</bean>

	<!-- activator bean for register page info to database at bundle start time. -->
	<bean id="bndActivator" class="com.gfactor.service.BndActivator"
		init-method="start" destroy-method="stop"></bean>

</beans>
這邊最主要的問題是context:load-time-weaver, tx:annotation-driven的設定,如果import-package有缺少的話並不一定會有exception,但是transaction可能因此失效,
後面會提到碰到過的issue,再來是完整的MANIFEST.mf 
 
Manifest-Version: 1.0
Export-Package: com.gfactor.service;version="1.0.0";uses:="org.springframework.beans.factory.annotation",
 com.gfactor.service.iface;version="1.0.0";uses:="com.gfactor.service.jpa",
 com.gfactor.service.jpa;version="1.0.0";uses:="javax.persistence"
Unversioned-Imports: *
Built-By: momo
Tool: Bnd-1.43.0
Bundle-Name: Spring OSGi Bundle for jpa bundle service layer
Created-By: Apache Maven Bundle Plugin
Bundle-Version: 1.0.0
Build-Jdk: 1.6.0_26
Bnd-LastModified: 1313477275968
Bundle-ManifestVersion: 2
Import-Library: 
 org.springframework.spring;version="[3.0.3, 3.1)"
Import-Package: com.mysql.jdbc,
 javax.persistence,
 javax.persistence.criteria,
 javax.persistence.metamodel, 
 javax.persistence.spi,
 javax.sql,
 org.aopalliance.aop,
 org.aspectj.lang,
 org.aspectj.runtime.reflect,
 org.springframework.aop,
 org.springframework.aop.config, 
 org.springframework.aop.aspectj,
 org.springframework.aop.aspectj.annotation,
 org.springframework.aop.aspectj.autoproxy,
 org.springframework.aop.framework,
 org.springframework.stereotype,
 org.springframework.core,
 org.springframework.context,
 org.springframework.context.config,
 org.springframework.context.weaving,
 org.springframework.beans, 
 org.springframework.beans.factory, 
 org.springframework.beans.factory.wiring,
 org.springframework.beans.factory.aspectj,
 org.springframework.beans.factory.support,
 org.springframework.beans.factory.config,
 org.springframework.beans.factory.annotation,
 org.springframework.dao.annotation,
 org.springframework.dao.support,
 org.springframework.orm.jpa,
 org.springframework.orm.jpa.support,
 org.springframework.orm.jpa.vendor, 
 org.springframework.transaction,
 org.springframework.transaction.interceptor,
 org.springframework.transaction.support,
 org.springframework.transaction.annotation,
 org.springframework.transaction.aspectj,
 org.springframework.instrument.classloading,
 org.eclipse.persistence.jpa,
 org.eclipse.persistence.expressions,
 org.eclipse.persistence.internal.weaving,
 org.eclipse.persistence.internal.descriptors,
 org.eclipse.persistence.queries,
 org.eclipse.persistence.sessions,
 org.eclipse.persistence.descriptors.changetracking,
 net.sf.cglib.proxy,
 net.sf.cglib.core,
 net.sf.cglib.reflect
Bundle-SymbolicName: plugin-webdomain-service.jpa-eplk

然後在BndActivator.java裡面做測試,在bundle start,如下
package com.gfactor.service;

import org.springframework.beans.factory.annotation.Autowired;

import com.gfactor.service.iface.IRegisterWicketPageBndIdentify;
import com.gfactor.service.jpa.Bndpageinfo;

public class BndActivator {
	
	@Autowired
	private IRegisterWicketPageBndIdentify registerWicketPageBndIdentify;
	
	public void start(){
		System.out.println("to get registerWicketPageBndIdentify = "  registerWicketPageBndIdentify);
		Bndpageinfo bndobj = new Bndpageinfo();
//		bndobj.setId(10);
		bndobj.setBundle_name("test");
		bndobj.setBundle_version("1.0.0");
		bndobj.setClass_name("testPage");
		bndobj.setEntry_point("test-main");
//		registerWicketPageBndIdentify.registerPageInfo(bndobj);
		registerWicketPageBndIdentify.unregisterPageInfo(bndobj);
//		regWicketPageBndDao.update(bndobj);
		
//		List&lt;Bndpageinfo&gt; getobj =  regWicketPageBndDao.findUser("test2", "1.0.1", "mains") ;
//		System.out.println("getobj = "  getobj);
	}
	
	public void stop(){
		
	}

}
沒問題的話可以在log看到transaction相關的log information,
to get registerWicketPageBndIdentify = com.gfactor.service.iface.internal.impl.RegisterWicketPageBndIdentifyImpl@158adc7 
[2011-08-18 16:21:56.625] DEBUG region-dm-8                  o.s.transaction.annotation.AnnotationTransactionAttributeSource   Adding transactional method 'unregisterPageInfo' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '' 
[2011-08-18 16:21:56.625] DEBUG region-dm-8                  o.s.beans.factory.support.DefaultListableBeanFactory              Returning cached instance of singleton bean 'transactionManager' 
[2011-08-18 16:21:56.625] DEBUG region-dm-8                  org.springframework.orm.jpa.JpaTransactionManager                 Creating new transaction with name [com.gfactor.service.iface.internal.impl.RegisterWicketPageBndIdentifyImpl.unregisterPageInfo]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '' 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        [EL Info]: 2011-08-18 16:21:56.625--ServerSession(21139682)--Thread(Thread[region-dm-8,5,main])--EclipseLink, version: Eclipse Persistence Services - 2.3.0.v20110604-r9504 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        [EL Config]: 2011-08-18 16:21:56.625--ServerSession(21139682)--Connection(30247146)--Thread(Thread[region-dm-8,5,main])--connecting(DatabaseLogin( 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        	platform=&gt;MySQLPlatform 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        	user name=&gt; "" 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        	connector=&gt;JNDIConnector datasource name=&gt;null 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        )) 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        [EL Config]: 2011-08-18 16:21:56.625--ServerSession(21139682)--Connection(9801033)--Thread(Thread[region-dm-8,5,main])--Connected: jdbc:mysql://127.0.0.1:3306/test 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        	User: root@localhost 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        	Database: MySQL  Version: 5.5.10 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        	Driver: MySQL-AB JDBC Driver  Version: mysql-connector-java-5.1.15 ( Revision: ${bzr.revision-id} ) 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        [EL Config]: 2011-08-18 16:21:56.625--ServerSession(21139682)--Connection(7085189)--Thread(Thread[region-dm-8,5,main])--connecting(DatabaseLogin( 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        	platform=&gt;MySQLPlatform 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        	user name=&gt; "" 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        	connector=&gt;JNDIConnector datasource name=&gt;null 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        )) 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        [EL Config]: 2011-08-18 16:21:56.625--ServerSession(21139682)--Connection(30977512)--Thread(Thread[region-dm-8,5,main])--Connected: jdbc:mysql://127.0.0.1:3306/test 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        	User: root@localhost 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        	Database: MySQL  Version: 5.5.10 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        	Driver: MySQL-AB JDBC Driver  Version: mysql-connector-java-5.1.15 ( Revision: ${bzr.revision-id} ) 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        [EL Info]: 2011-08-18 16:21:56.625--ServerSession(21139682)--Thread(Thread[region-dm-8,5,main])--file:/C:/Eclipse-Virgo-Tomcat-3.0/work/org.eclipse.virgo.kernel.deployer_3.0.0.RC1/staging/global/bundle/plugin-webdomain-service.jpa-eplk/1.0.0/plugin-webdomain-service.jpa-eplk.jar/_plugin-web-domain login successful 
[2011-08-18 16:21:56.625] DEBUG region-dm-8                  org.springframework.orm.jpa.JpaTransactionManager                 Opened new EntityManager [org.eclipse.persistence.internal.jpa.EntityManagerImpl@33d742] for JPA transaction 
[2011-08-18 16:21:56.625] DEBUG region-dm-8                  org.springframework.orm.jpa.JpaTransactionManager                 Exposing JPA transaction as JDBC transaction [SimpleConnectionHandle: jdbc:mysql://127.0.0.1:3306/test, UserName=root@localhost, MySQL-AB JDBC Driver] 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        [EL Fine]: 2011-08-18 16:21:56.625--ClientSession(33415531)--Connection(14317413)--Thread(Thread[region-dm-8,5,main])--SELECT ID, bundle_name, bundle_version, class_name, entry_point FROM bndpageinfo WHERE (((bundle_name = ?) AND (bundle_version = ?)) AND (entry_point = ?)) 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        	bind =&gt; [test, 1.0.0, test-main] 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        unregisterPageInfo , check findObj size = 0 
[2011-08-18 16:21:56.625] INFO  region-dm-8                  System.out                                                        no page info delete , object doesn't exists. 
[2011-08-18 16:21:56.625] DEBUG region-dm-8                  org.springframework.orm.jpa.JpaTransactionManager                 Initiating transaction commit 
[2011-08-18 16:21:56.625] DEBUG region-dm-8                  org.springframework.orm.jpa.JpaTransactionManager                 Committing JPA transaction on EntityManager [org.eclipse.persistence.internal.jpa.EntityManagerImpl@33d742] 
[2011-08-18 16:21:56.625] DEBUG region-dm-8                  org.springframework.orm.jpa.JpaTransactionManager                 Closing JPA EntityManager [org.eclipse.persistence.internal.jpa.EntityManagerImpl@33d742] after transaction 
[2011-08-18 16:21:56.625] DEBUG region-dm-8                  org.springframework.orm.jpa.EntityManagerFactoryUtils             Closing JPA EntityManager 

issue resolve :

整合中碰到的一個exception,
org.aspectj.weaver.tools.jdk14trace error : cannot register non aspect...,然後指向一個class , annotationasyncexecutionaspect,後來找到的問題出在Manifest.mf檔的package,從 Spring transactions with aspectj on Virgo這邊看到相關的問題,這種狀況在Transaction 為jdk proxy 時不會出問題,換成aspectj 後就會找不到transaction,當然最常出現的都是dependency 的問題:xD


Reference:

Wicket, Spring 3, JPA2 &amp; Hibernate OSGi Application on Apache Karaf
 
Creating custom Hibernate OSGi bundles for JPA 2.0
 
WCreating an application with EclipseRT Virgo Web Server
 
EclipseLink with Spring - Can't persist into Db
 
在Eclise Virgo 上运行Struts2+Spring3+hibernate3.5

2011年8月11日 星期四

OSGI : the manifest.mf header

OSGI bundle 包含了一個MANIFEST.mf檔, 這個檔描述了java classes 跟其他resource的定義,還有一些osgi相關的header內容, 一個簡單的OSGi manifest.mf 檔如下(from wiki)
Bundle-Name: Hello World
Bundle-SymbolicName: org.wikipedia.helloworld
Bundle-Description: A Hello World bundle
Bundle-ManifestVersion: 2
Bundle-Version: 1.0.0
Bundle-Activator: org.wikipedia.Activator
Export-Package: org.wikipedia.helloworld;version="1.0.0"
Import-Package: org.osgi.framework;version="1.3.0"

這邊針對OSGi 相關的MANIFEST.mf檔的header做簡單的說明


Bundle-Name:
Bundle-Name 定義一個human-readable的名稱,一般可定義一個簡單的名稱來辦別bundle, 沒太大的意義, 不影響bundle

Bundle-SymbolicName:
這個header在manifest.mf檔裡是必要的, 這個header會定義一個唯一的identifier給這個bundle,identifier會跟Bundle-Version做組合,格式如下
Bundle-SymbolicName_Bundle-Version

Bundle-Version:
定義bundle的版本號, 下圖表示了OSGI version number 的format(from OSGI in Action)
比如1.2.3.alpha , 1為大版本號,2為中版本,3為小版本, alpha 為1.2.3版的某個階段


Export-Package
透過Export-Package, 可以針對bundle裡的package export 給其他bundle使用, 一個例子如下
Export-Package: org.foo.shape; vendor="Manning", org.foo.other;
vendor="Manning"
這邊定義了一個屬性vendor,為一個arbitrary attribute, 此屬性是沒有任何意義,OSGi 會忽略此屬性(OSGI in action裡面的說明是如此, vendor is an arbitrary attribute because it has no special
meaning to the framework.),這邊要注意Export-package 不會將子package也一起export出去, 如果有com.bat 跟com.bat.abc , com.bat export後是不包含com.bat.abc的。

Bundle-ClassPath
定義bundle的classes path, 比如說Bundle-ClassPath: .,/WEb-INF/lib,embedded.jar
會指定. 根目錄(必須), 以及/WEB-INF/lib 為classes 的路徑和embedded.jar, "." 為bundle JAR file的root, 在這狀況下bundle會先search rool-relative package, 然後再尋找其他的classes如/WEB-INF/lib下, 接著是embedded.jar裡面.下圖的rule很接近,不過要考慮在bundle範圍內

Import-Package
定義了bundle要import 的package list, 下面是一個Export-Package的定義,
Export-Package: org.foo.shape; vendor="Manning", org.foo.other;
vendor="Manning"
所以我們在import package的時候能這樣做,
Import-Package: org.foo.shape; vendor="Manning"
則Manning 這個vendor屬性會被加到import-package裡面。
下圖是import & export package時會用到的Version Syntax,

下圖是一個完整的Bundle 引用
如果一個bundle 不在active狀態下,service是無法被引用的,但是如果只是要使用其classes的話,這個bundle只要處於resolved狀態就行,引用的classes在什麼時候失效?一旦A bundle從 B bundle引用了一個class成功,當B bundle被stop ,這時A bundle的class引用是還有效的,除非A bundle被refresh時才會解除。

DynamicImport-Package
定義需要動態調用的package list, 比如說一個JDBC driver class,需要調用而不是實現它,這時可以利用DynamicImport-Package, 如果我們設定一個屬性如下
DynamicImport-Package: *
這個將會動態導入任何bundle需要的package,而動態導入的search rule如下,
1:Requests for classes in "java." packages, 委託給parent classloader
2:Requests for classes in an imported package,委託給exporting bundle
3:The bundle class path is searched for the class
4:Requests for classes matching a dynamically imported package are delegated to an
exporting bundle if one is found;

(from OSGi Service Platform Release 4 Version 4.2 Core Specification : 3.8.2 Dynamic import package :
Dynamic importing implies inter-bundle constraints. That is, when a bundle A loads a class from a bundle B using dynamic importing, A is then considered to be dependent on B. If B is refreshed or uninstalled, A is refreshed. This behavior is valuable for maintaining consistency when A actually uses and retains references to B’s classes. However, several serialization scenarios have A simply using B’s classes temporarily (e.g., to load some object stream)—there should be no lasting dependency. )


Fragment-Host
一個bundle如果定義為Fragment的話, 代表它是依附在另一個bundle之上, Fragment-Host屬性為要依附的bundle, fragment bundle 必須要有一個宿主,無法獨立存在,而且使用的是宿主的classloader,
如下圖所示


Require-Bundle
使用require-bundle 會將其export 出來的pakcage 全部import進來, 如下圖

Bundle-ManifestVersion
代表OSGI參考的版本, 2 代表的OSGI R4.0+


Reference:

Wiki OSGI
Creating OSGi bundles