提供支撑的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