提供支撐的Bundles
--- 瀟湘振宇 2010-06-26
上一篇中,我們已經為這基于OSGi的Web應用搭建了項目框架,但其中涉及到有幾個支撐OSGi環境下的SSH框架的Bundles的改造或提供未曾給大家詳細介紹。本篇內容就是為詳細介紹這幾個Bundles的作用及創建的過程。
CGLIB的改造
Cglib的改造曾在系列一中有提到,為了解決Hibernate在調用CGLIB來創建代理對象時找不到HibernateProxy對象的問題。改造也相當的簡單,只需要將AbstractClassGenerator類中的getClassLoader方法中的ClassLoad改為當前類所在的ClassLoad即可,如下所示:
public ClassLoader getClassLoader() {
ClassLoader t = classLoader;
/**
* FIXME 更改classLoad的加載順序
* if (t == null) {
* t = getDefaultClassLoader();
* }
* if (t == null) {
* t = getClass().getClassLoader();
* }
*/
if (t == null) {
t = getClass().getClassLoader();
}
if (t == null) {
t = getDefaultClassLoader();
}
if (t == null) {
t = Thread.currentThread().getContextClassLoader();
}
if (t == null) {
throw new IllegalStateException("Cannot determine classloader");
}
return t;
}
|
以上是改造的內容,而我們在項目中應用的時候需要的是一個OSGi的Bundle,因此我們可以通過Pax-construct來創建這個Bundle。
大致過程是:首先在org.ideawork.osgi工程下面新建名為warpper的模塊,再到warpper目錄下面新建cglib的bundle工程,并將其導入到IDE環境中。然后從官方網站下載cglib的源碼,將源碼添加到相應的工程,再對需要修改的地方修改之。
具體步驟如下:
1. 新建warpper的模塊
通過Pax-Construct的命令來新建此warpper模塊,如下:
>cd osgiapp
>pax-create-module -g org.ideawork.osgiapp -a osgiapp-warpper -v 1.0.0
|
其中osgiapp為當前這應用的根目錄,cd osgiapp為進入到根目錄下,而下面的命令則是創建module工程,這在上篇已經介紹過。
2. 新建cglib的bundle
Cglib的bundle工程的創建跟上篇中其他bundle的創建類似,命令如下:
>cd osgiapp-warpper
>pax-create-bundle -p org.ideawork.osgiapp.warpper.cglib -n osgiapp-warpper-cglib -g org.ideawork.osgiapp.warpper -v 2.1.3-001
|
這里之所以要指定版本號為2.1.3-001是因為我們改造的是2.1.3版的cglib,而后面的001代表的是我們自己的warpper工程的小版本號,如果以后有對此cglib bundle升級,但cglib的版本還是2.1.3,那時候我們就升級001這個版本號。
3. 導入IDE(Eclipse)
導入方式如上篇所述,導入后的視圖如下:

4. 添加cglib的源碼
我們將從官方網站下載的cglib的源碼添加到cglib bundle下的src/main/java目錄下,并將新建此bundle工程時默認生成的org.ideawork.osgiapp.warpper.cglib的包刪除掉。刷新當前項目,此時會出現很多的紅叉叉,這是因為缺少相關的依賴包所致。因此我們需要在此bundle工程下的pom.xml文件中添加如下依賴信息:
<dependencies>
<dependency>
<groupId>org.objectweb.asm</groupId>
<artifactId>com.springsource.org.objectweb.asm</artifactId>
<version>1.5.3</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.objectweb.asm</groupId>
<artifactId>
com.springsource.org.objectweb.asm.attrs
</artifactId>
<version>1.5.3</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.objectweb.asm</groupId>
<artifactId>com.springsource.org.objectweb.asm.util</artifactId>
<version>2.2.0</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.codehaus.aspectwerkz</groupId>
<artifactId>com.springsource.org.codehaus.aspectwerkz.hook</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>com.springsource.org.apache.tools.ant</artifactId>
<version>1.7.0</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
</dependencies>
|
5. 修改ClassLoad加載順序
修改AbstractClassGenerator類中的getClassLoader方法中的ClassLoad加載順序已經在上面描述過,大家按上面的修改即可。
6. 完善MANIFEST.MF文件中的元數據信息
這里關于MANIFEST.MF文件中的一些元數據,我們不想讓bnd為我們來生成,因為通過bnd工具生成的元數據,有時候生成的元數據過于龐大。因此我們在osgi.bnd文件中添加相關的元數據信息,并刪除新建項目時默認生成的Bundle-Activator元素,需要添加的內容如下:
Export-Package = net.sf.cglib.asm;version="2.1.3","
net.sf.cglib.asm.attrs;version="2.1.3";uses:="net.sf.cglib.asm","
net.sf.cglib.beans;version="2.1.3";uses:="net.sf.cglib.asm,net.sf.cglib.core","
net.sf.cglib.core;version="2.1.3";uses:="net.sf.cglib.asm","
net.sf.cglib.proxy;version="2.1.3";uses:="net.sf.cglib.asm,net.sf.cglib.core,net.sf.cglib.reflect","
net.sf.cglib.reflect;version="2.1.3";uses:="net.sf.cglib.asm,net.sf.cglib.core","
net.sf.cglib.transform;version="2.1.3";uses:="net.sf.cglib.asm,net.sf.cglib.core,org.apache.tools.ant,org.apache.tools.ant.types","
net.sf.cglib.transform.hook;version="2.1.3";uses:="net.sf.cglib.asm,net.sf.cglib.transform","
net.sf.cglib.transform.impl;version="2.1.3";uses:="net.sf.cglib.asm,net.sf.cglib.core","
net.sf.cglib.util;version="2.1.3";uses:="net.sf.cglib.asm,net.sf.cglib.core"
Bundle-ClassPath = .
Bundle-Version = 2.1.3
JarJarStringTransformer = com.tonicsystems.jarjar.GeneratedStringTransformer
Bundle-Name = CGLIB Code Generation Library
Import-Package = org.apache.tools.ant;version="[1.7.0, 2.0.0)";resolution:=optional,"
org.apache.tools.ant.types;version="[1.7.0, 2.0.0)";resolution:=optional,"
org.codehaus.aspectwerkz.hook;version="[0.8.1, 0.9.0)";resolution:=optional,"
org.objectweb.asm;version="[1.5.3, 2.0.0)";resolution:=optional,"
org.objectweb.asm.attrs;version="[1.5.3, 2.0.0)";resolution:=optional
DynamicImport-Package = *
|
7. 編譯打包
編譯打包的工作我們是交給Maven來幫我們做,但我們需要修改cglib bundle工程下的pom.xml文件中的properties元素的相關屬性內容做。需要將<bundle.namespace>org.ideawork.osgiapp.warpper.cglib</bundle.namespace>修改成<bundle.namespace>net.sf.cglib</bundle.namespace>,這樣修改的目的是因為在利用Maven編譯打包的時候,bnd插件會根據bundle工程的namespace的定義來導出相關的包,如果項目中的classis文件不在其namespace范圍內,打包時被會被忽略掉。
修改完pom.xml文件后,在命令行窗口下進入到cglib bundle工程的目錄下,運行mvn -e clean install命令就可以將cglib bundle安裝到本地Maven倉庫。到這里cglib的改造完成。
封裝Mysql Connector
因為數據庫用的是Mysql,數據庫連接池使用的是C3p0,因為C3p0在創建數據庫連接池的時候需要使用到指定的驅動包,而C3p0作為一個獨立的Bundle,不能偶合具體的驅動程序包,所以C3p0在創建數據庫連接池時,需要我們提供一個Fragment來讓其導入我們指定的驅動程序包,這曾在系列一中也已經說到過。但這里我們將不通過提供另一個Fragment來實現此需求,因為我想使用最新版的Mysql connector 5.1.13,但這個版本的Mysql connector在EBR倉庫中尚不存在,因此我借這個封裝Mysql connector的機會來解決C3p0使用mysql的驅動包的問題,并向大家介紹如何使用pax-construts來創建warpper工程。
大致流程是:在命令行窗口下,進入到osgiapp/osgiapp-warpper目錄下,運行pax-construts創建warpper工程的命令,然后修改相關的配置文件,編譯打包即可。
具體步驟如下:
1. 創建osgiapp-warpper-mysqlconnector
這里的mysqlconnector跟與面的cglib都是通過改造第三方的包來達到目的的,因此我們將其統統放在了warpper模塊之中,但這里的mysqlconnector與cglib還是有區別的,cglib是通過pax-construts的pax-create-bundle命令創建的,而這里我們將通過pax-wrap-jar的命令來創建。命令如下:
這里的-g、-a、-v跟之前的含義是一樣的,分別表示groupId、artifactId、version,但這里的這些指是的將要被我們包裝的Jar包的Maven屬性。如果此Jar包不存在本地的Maven倉庫中,Maven將會從網上的Maven2的倉庫中下載此Jar文件。
2. 修改相關配置
成功創建工mysql-connector的warpper工程后,我們需要修改的有pom.xml文件跟osgi.bnd文件。
修改pom.xml文件中的artifactId為osgiapp-warpper-mysql-connector-java,并將version改成5.1.13-001,還有就是properties中的bundle.symbolicName成改org.ideawork.osgiapp.wrapper.mysql.connector.java
另外在osgi.bnd文件中添加如下信息:
pax-wrap-jar -g mysql -a mysql-connector-java -v 5.1.13
|
3. 編譯打包
編譯打包直接運行Maven的命令即可。
為Tomcat提供Fragment
為了使此web應用能正常訪問,我們需要為Tomcat 相關的兩個Bundle提供兩個Fragment。這在系列一中曾有說到。
需要使用到這兩個Fragment的Bundle的artifactId分別是:com.springsource.org.apache.catalina跟catalina.start.osgi,前者是apache公司下的包,后者是springframework為springDM的web支持而提供的。
具體的創建步驟如下:
1. 新建fragment的模塊
通過Pax-Construct的命令來新建此fragment模塊,如下:
Import-Package = ""
DynamicImport-Package = *
Fragment-Host = com.springsource.com.mchange.v2.c3p0
|
2. 新建兩個Fragment的bundle工程
Cglib的bundle工程的創建跟上篇中其他bundle的創建類似,命令如下:
3. 修改配置文件
將新建的Fragment工程導入到IDE環境,導入后工程視圖如下:

這里我們只需簡單的修改一下兩個Fragment Bundle工程下的osgi.bnd文件并在工程的資源文件夾下添加幾個配置文件即可。
a) 對于osgiapp-fragment-tomcat-conf這個Fragment,需要為其添加以下這四個配置文件,如圖:

這四個配置文件來自于普通的tomcat安裝包中,不過需要版本匹配才行。比如這里用到的tomcat bundle是6.0.15的那就需要從6.0.15版的傳統的tomcat的安裝目錄下拷貝。
然后修改其osgi.bnd文件,此osgi.bnd文件中的內容如下:
>cd osgiapp
>pax-create-module -g org.ideawork.osgiapp -a osgiapp-fragment -v 1.0.0
|
>cd osgiapp-fragment
>pax-create-bundle -p org.ideawork.osgiapp.fragment.tomcat.conf -n osgiapp-fragment-tomcat-conf -g org.ideawork.osgiapp.fragment -v 1.0.0
>pax-create-bundle -p org.ideawork.osgiapp.fragment.tomcat.start.conf -n osgiapp-fragment-tomcat-start-conf -g org.ideawork.osgiapp.fragment -v 1.0.0
|
此處的Fragment-Host=com.springsource.org.apache.catalina指當前這個Fragment的宿主為com.springsource.org.apache.catalina的bundle,Fragment-Host的值表示的是宿主Bundle的Bundle-SymbolicName。
b) 對于osgiapp-fragment-tomcat-start-conf 的Fragment,需要像上面的一樣添加一個server.xml的配置文件,這個文件的來由跟上面所說的一樣。添加后的視圖如下:

然后再修改其osgi.bnd文件,內容如下:
Export-Package = ""
Private-Package =
Import-Package = ""
Fragment-Host = com.springsource.org.apache.catalina
|
總結
通過以上的操作得到四個bundle,這四個bundle是這個OSGi的web應用所必須的,當然這樣的車輪只需要造一次,當你將需要再新建此類基于OSGi環境的SSH Web工程時,就可以直接拿過來用了。其中Tomcat的兩個Fragment主要是主含些配置文件,這個在項目部署的時候可能會需要修改的,具體得視應用而定。
完成了這章中的四個Bundle后,我們可以將這四個Bundle應用于系列三中的框架中來進行測試。也許這章的內容應該位于上篇之前介紹,大家就當這是對前一篇的補充吧。
附件:項目源碼 osgiapp.zip