[本文转自:http://www.ibm.com/developerworks/cn/web/0907_osgiweb_liuqing/#download]
2009 年 7 月 23 日
OSGi 作为新的事实工业标准正在各领域蓬勃发展起来,而 Web 开发技术则早已作为软件行业的主流技术被普遍使用着。传统的 Web 开发人员如何将 Web 开发与 OSGi 技术结合起来从中获益则是我们要关注的重点。通过本教程的学习,您可以全面掌握使用 Equinox 框架进行 OSGi 环境下 Web 开发的两种方式。
OSGi 的本质是将 Java 面向对象的开发转向面向组件和服务的开发。OSGi 框架提供了一套完善的机制用于管理和控制组件(Bundle)、服务(Service)的生命周期,以及组件和服务在其生命周期内的交互。OSGi 在提出核心框架规范的同时为一些常用的服务如日志服务(LogService),配置管理服务(ConfigAdmin),事件管理服务(EventAdmin),HTTP 服务(HTTPService)等提供了实现。
OSGi 框架在 2006 年之前还不为广大的开发者所知,最初的 OSGi 标准主要应用于 J2ME 和 J2SE。Equinox 项目是 Eclipse 开源组织提供的 OSGi 框架的实现。Equinox 的加入使得 OSGi 标准的应用领域不断扩充, Equinox 不仅提供了大部分 OSGi 标准服务的 bundle 实现,还借助 Eclipse 环境的一些自身的特点,提供了很多功能扩展服务。
Equinox 实现了 OSGi 在 J2ME、J2SE 方面的应用的同时,也推动了 OSGi 在 J2EE 方面的应用。Equinox 提供了一组基础的 Bundle,使得使用 JSP、Servlet 和 Struts 等 J2EE 技术的 Web 应用项目可以运行于 EquinoxOSGi 环境中。同样的, Equinox 通过一组 Bundle,可以将 Equinox OSGi 应用嵌入到现有的 Web 服务器(如 Tomcat,Jetty 等)和应用服务器(如 Websphere,Weblogic 等)中。这就是我们使用 Equinox 进行 Web 开发的两种方式。
本文的内容就是围绕 Equinox 针对 OSGi 在 J2EE 领域的实现而展开的。我们将在本文中结合实例详细的介绍如何应用 Equinox 框架将传统的 Web 开发和 OSGi 技术更好的结合起来。
|
|
工欲善其事,必先利其器。开始前先让我们来搭建我们的开发环境,为了减少因为环境不一致而引起的问题,建议读者使用与本文相同的软件版本:
下载并安装 JDK( 本文使用版本为 Sun JDK 5) ;
下载并解压 Eclipse( 本文使用版本为 Eclipse Ganymede J2EE SR2 版本 );
下载并安装 Tomcat( 本文使用版本为 Tomcat6.0.18);
我们的开发环境搭建相当简单,经过上面三个步骤之后,只需要在 Eclipse 首选项中将我们的
Tomcat 配置一下即可。
接下来就让我们看一下用 Equinox 框架进行 Web 开发的第一种方式。
|
|
将 HTTP Server 置于 Equinox 框架中开发 Web 应用
这种方式是一种完全的 OSGi 方式,HTTP Server( 本例中为 Jetty) 作为一个 Bundle 运行在整个 Equinox 框架中。在这种开发方式下,我们需要首先建立一个 Eclipse 插件项目:打开 Eclipse 的插件开发视图,新建一个插件项目。
我们为新项目命名为 com.sample.web, 在项目属性设置中,我们将项目的运行 target 设置为 Equinox
在接下来的属性设置中,我们不要选择创建 Activator,具体的原因我们将在文章的后续部分讲到。
剩余步骤中按照默认设置即可完成项目的创建工作。这样一个插件项目就创建完毕了。
然后我们在项目的根目录下创建 WebRoot,img 和 jsp 几个文件夹分别用来放置普通资源文件 ( 例如图片文件 ) 和 JSP 文件。在 Java 源文件目录下创建一个简单的 Servlet: LoginServlet.java 现在,项目结构图如下:
这个时候大家会看到项目有编译错误,不要着急。我们先来看一下我们的 servlet 中的内容 :
package com.sample.web.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author Levin * @time 2009-5-29 */ public class LoginServlet extends HttpServlet{ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { //Web 开发场景一:使用 Request 对象得到得到客户端请求数据 String userName = request.getParameter("userName"); //Web 开发场景二:操作 Session request.getSession().setAttribute("userFromA", userName); //Web 开发场景三:使用 Response 返回 response.setCharacterEncoding("GBK"); response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<body>"); out.println("<head>"); out.println("<title> 登录返回页面 </title>"); out.println("</head>"); out.println("<body>"); out.println(userName + ", 您的 sessionId:" + request.getSession().getId()); out.println("</body>"); out.println("</html>"); out.flush(); out.close(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { doGet(request, response); } } |
这个例子虽然简单,却将传统 Web 开发方式中常用的场景,比如 request, response 和 session 的使用都涵盖其中了。项目有编译错误,是因为我们使用了 javax.servlet 包中的类,所以我们点击错误信息,Eclipse 会自动帮助我们修改项目的 MANIFEST.MF 文件,将需要的包添加到 Import-Package 中去。下面是我们项目中的 index.jsp 文件的内容
<HTML> <HEAD> <TITLE>My App Home</TITLE> <META http-equiv=Content-Type content="text/html; charset=iso-8859-1"> <% String javaVersion = System.getProperty("java.version"); %> </HEAD> <BODY> <p>Current JRE version: <font color="#FF0000"><%=javaVersion%></font></p> <p> <a href="/servlet/myfirstservlet?userName=userA"> Click to test seervlet </a> </BODY> </HTML> |
到目前为止,我们已经把项目需要的各种准备工作做好了,比如建立文件夹,建立相关 jsp 文件,servlet 文件等。但是此时的项目还是不能具备解析 http 请求,转发到相应资源文件或 jsp 及 servlet 上的能力的。为了使项目具备这个能力,通常有向 httpservice 中注册 servlet 以及使用扩展点两种方法。本例中采用较为简单并较为广泛使用的扩展点方法。
为了配置扩展点,我们可以在项目的根目录下手动建立一个 plugin.xml 或者打开 MANIFEST.MF 文件 , 在其 Overview 视图标签页上找到扩展点设置区域,点击 extensions 即可对其扩展点进行配置。
plugin.xml 文件的内容如清单 3 所示,我们在其中定义了两个扩展点,一个用来处理对资源文件例如图片文件等的请求,另外一个用来处理 JSP 和 Servlet 的请求。
<?xml version="1.0" encoding="UTF-8"?> <?eclipse version="3.4"?> <plugin> <extension point="org.eclipse.equinox.http.registry.resources"> <resource alias="/images" base-name="/WebRoot/img"> </resource> </extension> <extension point="org.eclipse.equinox.http.registry.servlets"> <servlet alias="/servlet/myfirstservlet" class="com.sample.web.servlet.LoginServlet" load-on-startup="true"> </servlet> <servlet alias="/jsp/*.jsp" class="org.eclipse.equinox.jsp.jasper.registry.JSPFactory:/WebRoot/jsp/"> </servlet> </extension> </plugin> |
通过扩展点的配置,我们还需要对 MANIFEST.MF 文件进行相应的修改,引入需要的 bundle 和 package。最终的 MANIFEST.MF 文件如下 :
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Web Plug-in Bundle-SymbolicName: com.sample.web;singleton:=true Bundle-Version: 1.0.0 Bundle-Vendor: SAMPLE Bundle-RequiredExecutionEnvironment: J2SE-1.5 Import-Package: javax.servlet,javax.servlet.http Require-Bundle: org.eclipse.equinox.jsp.jasper.registry, org.eclipse.equinox.http.registry |
Ok, 到现在终于将所有的设置和所需文件都准备就绪,看起来是时候来运行了。别急,还有最后一项配置。打开项目的运行配置窗口,先反选所有的 target platform, 然后再点击 Add Required Bundles 按钮。这样在运行时候 Eclipse 不会将所有的 bundle 都启动,而只选择性的启动我们在配置文件中配置的 bundle。
当我们在 Eclipse 的控制台中输入 ss 命令,看到我们的 com.sample.web 这个 bundle 的状态是 active 的时候,我们就可以启动浏览器来访问我们的 Web 应用了。
各位读者可以试着在你们的浏览器中输入下面的 URL 看看有什么现象发生 , 并结合我们刚才的 plugin.xml 中扩展点的配置,思考一下他们的因果关系。
http://localhost/images/1.jpg
http://localhost/jsp/index.jsp
http://localhost/servlet/myfirstservlet?userName=Levin
如果一切正常的话,访问 JSP,您将会看到类似图 9 的运行界面。
注:
在最后布署一步,如果com.sample.web已经启动,但在BROSWER仍然不能访问那么做以下尝试:
修改MANIFEST.MF文件:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Web Plug-in
Bundle-SymbolicName: com.sample.web;singleton:=true
Bundle-Version: 1.0.0
Bundle-Vendor: SAMPLE
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Import-Package: javax.servlet,javax.servlet.http
Require-Bundle: org.eclipse.equinox.jsp.jasper.registry,
org.eclipse.equinox.http.registry,
org.eclipse.help;bundle-version="3.4.1",
org.eclipse.help.webapp;bundle-version="3.4.1"
添加了: org.eclipse.help;bundle-version="3.4.1",
org.eclipse.help.webapp;bundle-version="3.4.1"
这两个,
然后再重新执行图七的步骤.
|
|
将 Equinox 置于 Servlet 容器中开发 Web 应用
这种开发方式和刚才不同,这种开发方式是将 Equinox 框架以及项目的 bundle 做成一个 War 文件部署到 Servlet 容器中,和以前我们做普通的 Web 开发一样,当做一个普通的 Web 应用运行。在这种方式下,使用了一种叫做 Servlet Bridge(Servlet 桥 ) 的技术,用户对该 Web 应用的 http 请求被 servlet 容器转发给该 Web 应用,Equinox 框架运行在该 Web 应用中,统一处理对于该应用的请求,并将请求转发给对应的 bundle。
采用这种方式开发,一般情况下首先需要到 Equinox 站点上 下载 bridge.war 文件,这个文件是 2007 年 4 月的版本。我们在前面创建工程的时候没有让大家选择创建 activator, 原因就是在于 activator 引用的有些类在这个版本的 bridge.war 中不存在。此外:在我们的 sample 项目中,需要支持 JSP,所以需要几个额外的 bundle。为了大家的方便,我另外做了一个 bridge.war, 里面包含这些需要的 bundle。大家可以直接源代码下载部分 下载 。
下载该 war 文件成功后,将其导入到我们的 Eclipse 工作空间中来。
接下来,我们将刚才创建的 sample 项目导出为一个 bundle:打开我们的 sample 项目的 build.proerties 文件,将我们需要的 WebRoot 文件夹及其子文件夹以及 plugin.xml 都选中。
然后将我们的 sample 项目右键导出为一个 Deployable plug-ins and fragments
其他按照缺省设置即可,这时候大家会看到 Eclipse 会在你指定的目录下生成 plugins /com.sample.web_1.0.0.jar 文件,将该 jar 文件放到刚才导入的 bridge 项目的 plugins 文件夹下。
在这种方式下,我们不需要去运行我们的 sample 项目 , 而是运行我们的 bridge 项目,选中 bridge 项目 , 右键 ->run as->run on server 将 bridge 项目运行到我们配置的 Tomcat 中去。由于我们并没有设置让我们的 sample 项目默认自动启动,所以大家在此处需要启动我们的 sample 项目的 bundle.
启动完成后,大家来访问一下如下的 URL,看看有什么现象。考虑一下在这种开发方式下,同样的 bundle,URL 和刚才第一种方式下的有什么不同?
http://localhost:8080/bridge/servlet/myfirstservlet?userName=Levin
http://localhost:8080/bridge/jsp/index.jsp
http://localhost:8080/bridge/images/1.jpg
如果一切正常的话,在访问 Servlet 的时候您将会看到类似下图的运行效果。Tocmat 的默认端口是 8080,而图 9 中的 Jetty 默认端口是 80。并且,由于所有的 bundle 都是部署在 bridge 项目中,所以访问的 URL 也变成了以 http://localhost:8080/bridge/ 为基路径了。
在实际的开发过程中,如果每次都需要将我们的项目导出为 jar 文件放置到 bridge 中,开发效率是非常低下的。上述的步骤其实只适合在项目开发完成以后,实际部署到服务器上的时候才需要。在我们在开发过程中我们可以采取以下办法来简化开发:
打开 bridge 项目的 WebContent/Web-INF/eclipse/configuration/config.ini 文件,在其 osgi.bundles 的配置中增加对我们 sample project 的引用,例如:
reference\:file\:D:/eclipse-jee-ganymede-SR2-win32/workspace/com.sample.web@start
这时如果我们选中 bridge 项目 , 右键 ->run as->run on server 将 bridge 项目运行到我们配置的 Tomcat 中去的话,bridge 会根据 config.ini 中的配置,自动将我们的 sample project 也当做一个 bundle 启动。
|
|
刚才我们通过学习,可以发现不同的 Web 应用可以做成相对独立的 bundle 部署在 OSGi 环境中同时运行并对外提供 Web 服务,那么这些 Web 应用的 bundle 之间能否共享 Session 中的数据呢?如果可以共享 Session 数据的话,又会给我们以后的开发工作带来什么启示呢?这些都是很有意思的话题,有兴趣的读者可以在阅读完本文后自己做一些实验来一探究竟。
|
|
我们知道很多 J2EE 框架,如 Struts, Spring MVC,Echo 和 DWR 等,其运行基础很相似,都是基于 Servlet。通过配置 Servlet 的 URL pattern,将 HTTP 请求转发到框架的 Servlet 然后再实行派发。在我们刚才的 Web 项目例子中,对普通资源文件,jsp 和 servlet 的支持就为我们在项目中整合其他 J2EE 框架提供了可能。有兴趣的读者可以自己尝试一下,本文不再赘述。
|
|
本文对 OSGi 环境下的 Web 开发技术做了一个简要的介绍 , OSGi 和 Web 的结合作为一种新型的开发方式值得研究的地方还很多。相信通过本文的示例项目,可以帮助您更快的掌握这种全新的开发方式并为以后的学习打下很好的基础。
|
|
本文所有示例均在 Windows XP SP3 系统中测试完成。您需要一台能流畅运行 Windows XP 系统的机器,除此之外您还需要一些工具才能使用本教程中的代码。所有这些工具都可以免费下载:
JDK1.5 或更高版本
Eclipse 3.4 或更高版本
Tomcat 6.0 或更高版本
|
|
SourceCode.zip | 3123KB | HTTP |
关于下载方法的信息 |
学习
获得产品和技术
刘庆, IBM 中国开发中心 Optim 团队开发工程师,在 J2EE 和 Web 开发领域有着多年开发经验,喜欢关注新技术。他毕业于中国科技大学,并拥有硕士学位。 |
朱志辉,拥有清华大学硕士学位,目前供职于 |