您现在的位置是:蓝石榴 > 个人博客 > OSGI

个人博客

OSGi原理与最佳实践--查询字典B/S类型系统

2014-09-04OSGI 7520
我们首先来看一下,如何基于OSGi来开发B/S结构的应用。B/S结构应用程序的开发,可有两个选择:一个是在OSGi的框架中嵌入Http服务器,另外一个是在Servlet容器中嵌入OSGi框架。下面分别介绍这两种方式的实现。
1.B/S

我们首先来看一下,如何基于OSGi来开发B/S结构的应用。B/S结构应用程序的开发,可有两个选择:一个是在OSGi的框架中嵌入Http服务器,另外一个是在Servlet容器中嵌入OSGi框架。下面分别介绍这两种方式的实现。此外,本节还会给出Declarative Service的使用实例。首先来构想一下这个B/S应用的功能。我们提供一个字典服务,用户在浏览器中输入一个单词,点击提交,给出这个单词的解释。当然,这个例子并不会真正细致地实现这个功能,比如不会真的做一个字典来提供服务,仅仅支持很少的单词的查询。有了这个例子,如果你有兴趣,可以来完善它。在这个例子中,我们会有四个Bundle,分别是字典查询响应Bundle,字典查询接口Bundle,本地字典服务Bundle,远程字典服务Bundle。设计示意图如图1-18所示。

下面分析介绍它们。

■字典查询响应Bundle

提供输入要查询单词的页面,接受用户的查询请求,从BundleContext 中获取字典服务的Service,调用字典服务的查询接口得到查询结果,并返回结果到页面。

■字典查询接口Bundle

对外提供字典查询的接口。

■本地字典服务Bundle

提供字典查询服务,是从本地的字典中查询结果。

■远程字典服务Bundle

提供字典查询服务,是从远程的字典中查询结果。好了,我知道大家已经迫不及待了。下面我们就来动手实现第一个B/S结构的应用。

2.OSGi框架嵌入Http服务器

这一节我们采用把Http服务器嵌入到OSGi框架中的方法来完成这个字典查询系统的开发。首先我们要准备一下环境。回忆一下,我们在前面讲到HelloWorld例子的时候,介绍过环境的准备。在HelloWorld的例子中,我们只需要一个系统的Bundle。现在我们的运行环境要比HelloWorld稍微复杂一些,我们需要更多的Bundle,下面先来准备一下我们的环境。我们在Run Configurations的对话框中创建一个新的OSGi Framework的运行配置,在这个配置的Bundles 中 选择下面几个Bundle:javax.servlet 、org.apache.commons.logging、org.eclipse.equinox.http.jetty 、org.eclipse.equinox.http.servlet 、org.eclipse.osgi 、org.eclipse.osgi.services 和org.mortbay.jetty,如图1-19所示。

点击Run,可以看到有些启动的日志输出。如果看到osgi>而且没有错误信息,则表明环境已经配置成功;而如果看到类似Address already in use: JVM_Bind,则说明本机的80端口已被占用,由于Equinox的Http Service实现默认使用的是80端口,所以会报这个错,那么我们可以指定Http Service使用的端口。我们打开Run Configurations中运行配置里面的Arguments页签,在VM arguments中添加“-Dorg.osgi.service.http.port=8080”就可以设置使用8080作为Http Service的端口了,本例子中仍采用默认的80端口,在正常启动后,输入ss,回车,可以看到如图1-20所示的显示。

说明我们的环境已经准备好了。我们可以打开浏览器,输入http://localhost/,提示见图1-21。

说明我们的服务器已经正常启动。下面就可以开始Bundle的开发工作了。

第一步,完成字典查询接口Bundle工程。

首先创建名为DictQuery的Plug-in工程(见图1-22)。

然后我们在这个工程的org.osgichina.demo.dictquery.query package中创建一个接口(见图1-23)。

因为这个Bundle是为了导出接口,所以这个Bundle的BundleActivator不做任何的改动。双击这个Bundle工程中META-INF下的MANIFEST.MF文件,在新的窗口中会显示这个MANIFEST的信息(见图1-24)。

可以看到这个窗口下有多个页签,其中Overview、Dependencies、Runtime、Build这四个页签是图形化的编辑页签,对应的修改会反映到MANIFEST.MF和build.properties文件中。MANIFEST.MF和build.properties这两个页签分别直接显示了MANIFEST.MF和build.properties文件的内容。下面来完成将接口的package从Bundle中导出,能够让其他的Bundle来使用这个接口的package。

首先选择Runtime页签,然后点击Exported Packages中的Add,在弹出的窗口中选择所创建的QueryService所在的package: org.osgichina.demo.dictquery.query,点击保存就完成导出Package的操作了(见图1-25)。

这个时候去看MANIFEST.MF文件,其中多了如下一行:

Export-Package: org.osgichina.demo.dictquery.query

到此,我们就完成了字典查询接口Bundle的开发了。接着,我们来完成本地字典查询Bundle的开发。

第二步,完成本地字典查询Bundle。

■首先,创建一个名字为LocalDictQuery的插件工程。

■然后,导入字典查询接口Bundle,并且实现一个真正的字典查询的类。我们打开LocalDictQuery工程中META-INF下的MANIFEST.MF文件,选择Dependencies页签。点击右侧Imported Packages的Add按钮,在弹出的对话框中选择前面请求处理接口Bundle导出的那个Package:org.osgichina.demo.dictquery.query,保存后就完成了导入的操作。我们可以查看一下请求处理Bundle的MANIFEST.MF文件,在Import-Package项中,多了org.osgichina.demo.dictquery.query(见图1-26)。

■接着,我们来编写LocalDictQueryServiceImpl的代码,这个是实现了QueryService接口的一个类。LocalDictQueryServiceImpl中的queryWord方法,可从本地初始化的字典中查询结果;如果没有结果,返回一个“N/A”,代码如图1-27所示。

■最后,我们要编写Activator的代码(见图1-28),在Bundle启动的时候注册我们提供的字典查询服务。从开发角度来看,Service有点像虚拟的概念,因为在编写Service 时和编写普通的Java类(POJO)没有任何区别,这个工程中的LocalDictQueryServiceImp就是一个Service类。那么如何能够让其他的Bundle使用这个Service?在OSGi的框架中,我们要通过BundleContext来进行服务的注册,并且后面会看到在其他的Bundle中如何拿到这个Service实例来使用。

可以看到,在自动生成的Activator中,我们改动了两处。一个是我们加入了一个类型是ServiceRegistration的成员变量sr,一个是我们在start和stop方法中分别加入了一行代码,下面我们来看这行代码的含义。

sr = context.registerService(QueryService.class.getName(),new LocalDictQueryServiceImpl(), null);

上面这行代码是是用QueryService的全类名作为注册的Service 的名称,把新创建的LocalDictQueryServiceImpl对象注册成为了一个Service。而这个Service对象将在下面演示如何被使用。

sr.unregister();

以上这行代码是取消注册的Service。到这里,我们就完成了请求处理Bundle的代码编写。

第三步,完成实现远程字典查询Bundle。

Bundle和LocalDictQuery Bundle非常地类似,只是工程名称为RemoteDictQuery,另外为了能够显示区别,这个Bundle中提供服务的类的代码有所变化。实现QueryService接口的类的代码如图1-29所示。

最后,我们完成字典查询响应Bundle。

第四步,完成字典查询响应Bundle。

和传统的Web开发方式不同,由于OSGi 框架中并没有像web应用服务器那样的Bundle,就不能像web应用直接部署到web服务器那样简单了,要通过HttpService将Servlet及资源文件(像图片、css、html 等)进行注册,这样才可以访问。这里只是一个简单的Demo,提供一个字典查询的页面和对应的Servlet。我们在src目录下建立一个page的目录,在其中编写dictquery.htm,另外就是实现字典查询响应的Servlet,由于Servlet 要继承HttpServlet,要引用Servlet API。要引入javax.servlet 和javax.servlet.http 两个包,接着,除了要引入org.osgichina.demo.dictquery.query这个package外,还要一个org.osgi.service.http package,如图1-30所示。

然后,我们可以编写Servlet的代码,和普通的Servlet的写法没有差别。要解释的是在doGet方法中的这么一段代码:

ServiceReference serviceRef = context.getServiceReference(QueryService.class.getName());

if(null != serviceRef){

queryService = (QueryService) context.getService(serviceRef);

}

context是在创建Servlet的时候传入的BundleContext,要通过这个context来获取提供字典查询功能的服务。首先通过context获取Service的引用,返回的是一个ServiceReference对象。然后再通过ServiceReference获取Service实例。拿到Service实例后,就可以调用Service的方法来完成字典查询功能了。另外,在这个字典查询响应Bundle中还要做的一件事情就是在Bundle启动的时候,把Servlet注册到Http服务中去。这个代码是在BundleActivator中完成的。至此已经完成了字典查询系统的开发。下面来运行一下系统。启动之后我们在osgi>提示符下输入ss,回车,可以看到类似图1-31的显示。

打开浏览器,然后输入http://localhost/demo/page/dictquery.htm,将会显示如图1-32所示的内容。

下面,我们先执行stop 10(停掉RemoteDictQuery Bundle),然后输入test,点击查询。会有如下显示:

Result is 测试

并且我们可以在Eclipse的Console中看到如下的输出:

osgi> LocalDictQueryServiceImpl.queryWord called!

接着我们执行stop 11,回车,stop 10,回车。停止LocalDictQuery Bundle,并且启动RemoteDictQuery Bundle,然后在查询页面上输入sky,点击查询。会有如下显示:

Result is 天空

到这里,我们已经完成了字典查询系统的开发。

3.应用部署

我们已经完成了字典查询整个应用的开发、运行、调试和测试工作。不过这些都是在Eclipse这个开发工具中完成的,我们可不能给客户一个要客户在Eclipse上运行的应用,所以下面来看一下如何部署OSGi应用。

第一步,创建独立的Equinox运行环境。

在硬盘上创建一个osgi_demo目录,从Eclipse的plugins目录复制org.eclipse.osgi_3.4.3.R34x_ v20081215-1030.jar(在不同版本的Eclipse中,这个jar包的org.eclipse.osgi_后面的部分会有所不同)到这个osgi_demo目录。修改这个jar 包的名称为equinox.jar,然后在相同目录下编写一个run.bat,其内容如下:

java -jar equinox.jar -console

双击run.bat,如果出现osgi>的提示,则说明启动已经成功了。输入ss命令然后回车,这个时候会看到只有一个ACTIVE状态的system bundle。

第二步,导出各个Bundle工程为jar。

以最复杂的DictQueryWeb为例,首先打开MANIFEST.MF,选择Runtime页签,设置Classpath(见图1-34)。

然后选择Build页签,选中其中Binary Build里面的lib目录(见图1-35)。

接着,选中DictQueryWeb工程,点右键,选择Export,然后选中弹出对话框中的Deployable plug-ins and fragements(见图1-36)。

在进入的Deployable plug-ins and fragments窗口中已经默认选择了DictQueryWeb这个bundle,然后选择Destination标签页,设置一个有效的Directory,然后点击Finish,在设置的目录中可以看到一个plugins目录,在plugins目录中就有DictQueryWeb_1.0.0.jar这个bundle了。按照相同的方法可以导出其他的几个bundle,也可以一次性地把几个bundle 都导出来(见图1-37)。

第三步,安装各Bundle到Equinox中。

首先在osgi_demo目录下创建一个bundles目录,然后将第二步生成的三个bundle复制到bundles目录下,此外,我们要从Eclipse的plugins目录中把我们需要的

javax.servlet_2.4.0.v200806031604.jar

org.eclipse.equinox.http.servlet_1.0.100.v20080427-0830.jar

org.eclipse.equinox.http.jetty_1.1.0.v20080425.jar

org.mortbay.jetty_5.1.14.v200806031611.jar

org.apache.commons.logging_1.0.4.v20080605-1930.jar

org.eclipse.osgi.services_3.1.200.v20071203.jar

这几个Jar文件复制到Bundles目录中。

将Bundle安装到Equinox中:

运 行之前 编写 的run.bat,在osgi>中输入install reference:file:bundles/ DictQuery_1.0.0.jar,回车,这样就完成了DictQuery Bundle的安装。我们可以用同样的方法完成DictQueryWeb Bundle、LocalDictQueryBundle、RemoteDictQueryBundle及其他系统Bundle的安装。

然后在osgi>提示符下输入ss,回车,可以看到如图1-38所示的状态。

可见,目前我们安装后的10个Bundle都已经是INSTALLED 的状态。下面让我们来启动这些Bundle。首 先来启动系统的Bundle,依次启 动javax.servlet、org.apache.commons.logging、org.eclipse.osgi.services、org.mortbay.jetty、org.eclipse.equinox.http.servlet 和org.eclipse.equinox.http. jetty,然后启动我们自己开发的Bundle。可以在osgi>提示符下输入ss,会有如图1-39所示的输出。

这个时候,可以通过浏览器来测试我们的应用了。最后输入exit,就可以退出系统。以后只须双击run.bat就可以完成系统的启动。经过这样的步骤,就形成了单独运行的OSGi系统。

很赞哦!(1551)