簡介: OSGi 作為新的事實(shí)工業(yè)標(biāo)準(zhǔn)正在各領(lǐng)域蓬勃發(fā)展起來,而 Web 開發(fā)技術(shù)則早已作為軟件行業(yè)的主流技術(shù)被普遍使用著。傳統(tǒng)的 Web 開發(fā)人員如何將 Web 開發(fā)與 OSGi 技術(shù)結(jié)合起來從中獲益則是我們要關(guān)注的重點(diǎn)。通過本教程的學(xué)習(xí),您可以全面掌握使用 Equinox 框架進(jìn)行 OSGi 環(huán)境下 Web 開發(fā)的兩種方式。
本文的標(biāo)簽: ajax, java_技術(shù), javascript, jsp_(javaserver_pages)_technology, osgi, 應(yīng)用集成, 技巧
OSGi 的本質(zhì)是將 Java 面向?qū)ο蟮拈_發(fā)轉(zhuǎn)向面向組件和服務(wù)的開發(fā)。OSGi 框架提供了一套完善的機(jī)制用于管理和控制組件(Bundle)、服務(wù)(Service)的生命周期,以及組件和服務(wù)在其生命周期內(nèi)的交互。OSGi 在提出核心框架規(guī)范的同時(shí)為一些常用的服務(wù)如日志服務(wù)(LogService),配置管理服務(wù)(ConfigAdmin),事件管理服務(wù)(EventAdmin),HTTP 服務(wù)(HTTPService)等提供了實(shí)現(xiàn)。
OSGi 框架在 2006 年之前還不為廣大的開發(fā)者所知,最初的 OSGi 標(biāo)準(zhǔn)主要應(yīng)用于 J2ME 和 J2SE。Equinox 項(xiàng)目是 Eclipse 開源組織提供的 OSGi 框架的實(shí)現(xiàn)。Equinox 的加入使得 OSGi 標(biāo)準(zhǔn)的應(yīng)用領(lǐng)域不斷擴(kuò)充, Equinox 不僅提供了大部分 OSGi 標(biāo)準(zhǔn)服務(wù)的 bundle 實(shí)現(xiàn),還借助 Eclipse 環(huán)境的一些自身的特點(diǎn),提供了很多功能擴(kuò)展服務(wù)。
Equinox 實(shí)現(xiàn)了 OSGi 在 J2ME、J2SE 方面的應(yīng)用的同時(shí),也推動(dòng)了 OSGi 在 J2EE 方面的應(yīng)用。Equinox 提供了一組基礎(chǔ)的 Bundle,使得使用 JSP、Servlet 和 Struts 等 J2EE 技術(shù)的 Web 應(yīng)用項(xiàng)目可以運(yùn)行于 EquinoxOSGi 環(huán)境中。同樣的, Equinox 通過一組 Bundle,可以將 Equinox OSGi 應(yīng)用嵌入到現(xiàn)有的 Web 服務(wù)器(如 Tomcat,Jetty 等)和應(yīng)用服務(wù)器(如 Websphere,Weblogic 等)中。這就是我們使用 Equinox 進(jìn)行 Web 開發(fā)的兩種方式。
本文的內(nèi)容就是圍繞 Equinox 針對 OSGi 在 J2EE 領(lǐng)域的實(shí)現(xiàn)而展開的。我們將在本文中結(jié)合實(shí)例詳細(xì)的介紹如何應(yīng)用 Equinox 框架將傳統(tǒng)的 Web 開發(fā)和 OSGi 技術(shù)更好的結(jié)合起來。
工欲善其事,必先利其器。開始前先讓我們來搭建我們的開發(fā)環(huán)境,為了減少因?yàn)榄h(huán)境不一致而引起的問題,建議讀者使用與本文相同的軟件版本:
下載并安裝 JDK( 本文使用版本為 Sun JDK 5) ;
下載并解壓 Eclipse( 本文使用版本為 Eclipse Ganymede J2EE SR2 版本 );
下載并安裝 Tomcat( 本文使用版本為 Tomcat6.0.18);
我們的開發(fā)環(huán)境搭建相當(dāng)簡單,經(jīng)過上面三個(gè)步驟之后,只需要在 Eclipse 首選項(xiàng)中將我們的
Tomcat 配置一下即可。
接下來就讓我們看一下用 Equinox 框架進(jìn)行 Web 開發(fā)的第一種方式。
將 HTTP Server 置于 Equinox 框架中開發(fā) Web 應(yīng)用
這種方式是一種完全的 OSGi 方式,HTTP Server( 本例中為 Jetty) 作為一個(gè) Bundle 運(yùn)行在整個(gè) Equinox 框架中。在這種開發(fā)方式下,我們需要首先建立一個(gè) Eclipse 插件項(xiàng)目:打開 Eclipse 的插件開發(fā)視圖,新建一個(gè)插件項(xiàng)目。
我們?yōu)樾马?xiàng)目命名為 com.sample.web, 在項(xiàng)目屬性設(shè)置中,我們將項(xiàng)目的運(yùn)行 target 設(shè)置為 Equinox
在接下來的屬性設(shè)置中,我們不要選擇創(chuàng)建 Activator,具體的原因我們將在文章的后續(xù)部分講到。
剩余步驟中按照默認(rèn)設(shè)置即可完成項(xiàng)目的創(chuàng)建工作。這樣一個(gè)插件項(xiàng)目就創(chuàng)建完畢了。
然后我們在項(xiàng)目的根目錄下創(chuàng)建 WebRoot,img 和 jsp 幾個(gè)文件夾分別用來放置普通資源文件 ( 例如圖片文件 ) 和 JSP 文件。在 Java 源文件目錄下創(chuàng)建一個(gè)簡單的 Servlet: LoginServlet.java 現(xiàn)在,項(xiàng)目結(jié)構(gòu)圖如下:
這個(gè)時(shí)候大家會(huì)看到項(xiàng)目有編譯錯(cuò)誤,不要著急。我們先來看一下我們的 servlet 中的內(nèi)容 :
packagecom.sample.web.servlet; importjava.io.IOException; importjava.io.PrintWriter; importjavax.servlet.ServletException; importjavax.servlet.http.HttpServlet; importjavax.servlet.http.HttpServletRequest; importjavax.servlet.http.HttpServletResponse; /** * @authorLevin * @time2009-5-29 */ public classLoginServlet extendsHttpServlet{ public voiddoGet(HttpServletRequest request, HttpServletResponse response) throwsIOException, ServletException { //Web 開發(fā)場景一:使用 Request 對象得到得到客戶端請求數(shù)據(jù) String userName = request.getParameter("userName"); //Web 開發(fā)場景二:操作 Session request.getSession().setAttribute("userFromA", userName); //Web 開發(fā)場景三:使用 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 voiddoPost(HttpServletRequest request, HttpServletResponse response) throwsIOException, ServletException { doGet(request, response); } } |
這個(gè)例子雖然簡單,卻將傳統(tǒng) Web 開發(fā)方式中常用的場景,比如 request, response 和 session 的使用都涵蓋其中了。項(xiàng)目有編譯錯(cuò)誤,是因?yàn)槲覀兪褂昧?javax.servlet 包中的類,所以我們點(diǎn)擊錯(cuò)誤信息,Eclipse 會(huì)自動(dòng)幫助我們修改項(xiàng)目的 MANIFEST.MF 文件,將需要的包添加到 Import-Package 中去。下面是我們項(xiàng)目中的 index.jsp 文件的內(nèi)容
<HTML> <HEAD> <TITLE>My App Home</TITLE> <META http-equiv=Content-Typecontent="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> |
到目前為止,我們已經(jīng)把項(xiàng)目需要的各種準(zhǔn)備工作做好了,比如建立文件夾,建立相關(guān) jsp 文件,servlet 文件等。但是此時(shí)的項(xiàng)目還是不能具備解析 http 請求,轉(zhuǎn)發(fā)到相應(yīng)資源文件或 jsp 及 servlet 上的能力的。為了使項(xiàng)目具備這個(gè)能力,通常有向 httpservice 中注冊 servlet 以及使用擴(kuò)展點(diǎn)兩種方法。本例中采用較為簡單并較為廣泛使用的擴(kuò)展點(diǎn)方法。
為了配置擴(kuò)展點(diǎn),我們可以在項(xiàng)目的根目錄下手動(dòng)建立一個(gè) plugin.xml 或者打開 MANIFEST.MF 文件 , 在其 Overview 視圖標(biāo)簽頁上找到擴(kuò)展點(diǎn)設(shè)置區(qū)域,點(diǎn)擊 extensions 即可對其擴(kuò)展點(diǎn)進(jìn)行配置。
plugin.xml 文件的內(nèi)容如清單 3 所示,我們在其中定義了兩個(gè)擴(kuò)展點(diǎn),一個(gè)用來處理對資源文件例如圖片文件等的請求,另外一個(gè)用來處理 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> |
通過擴(kuò)展點(diǎn)的配置,我們還需要對 MANIFEST.MF 文件進(jìn)行相應(yīng)的修改,引入需要的 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, 到現(xiàn)在終于將所有的設(shè)置和所需文件都準(zhǔn)備就緒,看起來是時(shí)候來運(yùn)行了。別急,還有最后一項(xiàng)配置。打開項(xiàng)目的運(yùn)行配置窗口,先反選所有的 target platform, 然后再點(diǎn)擊 Add Required Bundles 按鈕。這樣在運(yùn)行時(shí)候 Eclipse 不會(huì)將所有的 bundle 都啟動(dòng),而只選擇性的啟動(dòng)我們在配置文件中配置的 bundle。
當(dāng)我們在 Eclipse 的控制臺(tái)中輸入 ss 命令,看到我們的 com.sample.web 這個(gè) bundle 的狀態(tài)是 active 的時(shí)候,我們就可以啟動(dòng)瀏覽器來訪問我們的 Web 應(yīng)用了。
各位讀者可以試著在你們的瀏覽器中輸入下面的 URL 看看有什么現(xiàn)象發(fā)生 , 并結(jié)合我們剛才的 plugin.xml 中擴(kuò)展點(diǎn)的配置,思考一下他們的因果關(guān)系。
http://localhost/images/1.jpg
http://localhost/jsp/index.jsp
http://localhost/servlet/myfirstservlet?userName=Levin
如果一切正常的話,訪問 JSP,您將會(huì)看到類似圖 9 的運(yùn)行界面。
將 Equinox 置于 Servlet 容器中開發(fā) Web 應(yīng)用
這種開發(fā)方式和剛才不同,這種開發(fā)方式是將 Equinox 框架以及項(xiàng)目的 bundle 做成一個(gè) War 文件部署到 Servlet 容器中,和以前我們做普通的 Web 開發(fā)一樣,當(dāng)做一個(gè)普通的 Web 應(yīng)用運(yùn)行。在這種方式下,使用了一種叫做 Servlet Bridge(Servlet 橋 ) 的技術(shù),用戶對該 Web 應(yīng)用的 http 請求被 servlet 容器轉(zhuǎn)發(fā)給該 Web 應(yīng)用,Equinox 框架運(yùn)行在該 Web 應(yīng)用中,統(tǒng)一處理對于該應(yīng)用的請求,并將請求轉(zhuǎn)發(fā)給對應(yīng)的 bundle。
采用這種方式開發(fā),一般情況下首先需要到 Equinox 站點(diǎn)上 下載bridge.war 文件,這個(gè)文件是 2007 年 4 月的版本。我們在前面創(chuàng)建工程的時(shí)候沒有讓大家選擇創(chuàng)建 activator, 原因就是在于 activator 引用的有些類在這個(gè)版本的 bridge.war 中不存在。此外:在我們的 sample 項(xiàng)目中,需要支持 JSP,所以需要幾個(gè)額外的 bundle。為了大家的方便,我另外做了一個(gè) bridge.war, 里面包含這些需要的 bundle。大家可以直接源代碼下載部分 下載。
下載該 war 文件成功后,將其導(dǎo)入到我們的 Eclipse 工作空間中來。
接下來,我們將剛才創(chuàng)建的 sample 項(xiàng)目導(dǎo)出為一個(gè) bundle:打開我們的 sample 項(xiàng)目的 build.proerties 文件,將我們需要的 WebRoot 文件夾及其子文件夾以及 plugin.xml 都選中。
然后將我們的 sample 項(xiàng)目右鍵導(dǎo)出為一個(gè) Deployable plug-ins and fragments
其他按照缺省設(shè)置即可,這時(shí)候大家會(huì)看到 Eclipse 會(huì)在你指定的目錄下生成 plugins /com.sample.web_1.0.0.jar 文件,將該 jar 文件放到剛才導(dǎo)入的 bridge 項(xiàng)目的 plugins 文件夾下。
在這種方式下,我們不需要去運(yùn)行我們的 sample 項(xiàng)目 , 而是運(yùn)行我們的 bridge 項(xiàng)目,選中 bridge 項(xiàng)目 , 右鍵 ->run as->run on server 將 bridge 項(xiàng)目運(yùn)行到我們配置的 Tomcat 中去。由于我們并沒有設(shè)置讓我們的 sample 項(xiàng)目默認(rèn)自動(dòng)啟動(dòng),所以大家在此處需要啟動(dòng)我們的 sample 項(xiàng)目的 bundle.
啟動(dòng)完成后,大家來訪問一下如下的 URL,看看有什么現(xiàn)象??紤]一下在這種開發(fā)方式下,同樣的 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 的時(shí)候您將會(huì)看到類似下圖的運(yùn)行效果。Tocmat 的默認(rèn)端口是 8080,而圖 9 中的 Jetty 默認(rèn)端口是 80。并且,由于所有的 bundle 都是部署在 bridge 項(xiàng)目中,所以訪問的 URL 也變成了以 http://localhost:8080/bridge/ 為基路徑了。
在實(shí)際的開發(fā)過程中,如果每次都需要將我們的項(xiàng)目導(dǎo)出為 jar 文件放置到 bridge 中,開發(fā)效率是非常低下的。上述的步驟其實(shí)只適合在項(xiàng)目開發(fā)完成以后,實(shí)際部署到服務(wù)器上的時(shí)候才需要。在我們在開發(fā)過程中我們可以采取以下辦法來簡化開發(fā):
打開 bridge 項(xiàng)目的 WebContent/Web-INF/eclipse/configuration/config.ini 文件,在其 osgi.bundles 的配置中增加對我們 sample project 的引用,例如:
reference\:file\:D:/eclipse-jee-ganymede-SR2-win32/workspace/com.sample.web@start
這時(shí)如果我們選中 bridge 項(xiàng)目 , 右鍵 ->run as->run on server 將 bridge 項(xiàng)目運(yùn)行到我們配置的 Tomcat 中去的話,bridge 會(huì)根據(jù) config.ini 中的配置,自動(dòng)將我們的 sample project 也當(dāng)做一個(gè) bundle 啟動(dòng)。
Bundle 間 Session 的數(shù)據(jù)共享
剛才我們通過學(xué)習(xí),可以發(fā)現(xiàn)不同的 Web 應(yīng)用可以做成相對獨(dú)立的 bundle 部署在 OSGi 環(huán)境中同時(shí)運(yùn)行并對外提供 Web 服務(wù),那么這些 Web 應(yīng)用的 bundle 之間能否共享 Session 中的數(shù)據(jù)呢?如果可以共享 Session 數(shù)據(jù)的話,又會(huì)給我們以后的開發(fā)工作帶來什么啟示呢?這些都是很有意思的話題,有興趣的讀者可以在閱讀完本文后自己做一些實(shí)驗(yàn)來一探究竟。
我們知道很多 J2EE 框架,如 Struts, Spring MVC,Echo 和 DWR 等,其運(yùn)行基礎(chǔ)很相似,都是基于 Servlet。通過配置 Servlet 的 URL pattern,將 HTTP 請求轉(zhuǎn)發(fā)到框架的 Servlet 然后再實(shí)行派發(fā)。在我們剛才的 Web 項(xiàng)目例子中,對普通資源文件,jsp 和 servlet 的支持就為我們在項(xiàng)目中整合其他 J2EE 框架提供了可能。有興趣的讀者可以自己嘗試一下,本文不再贅述。
本文對 OSGi 環(huán)境下的 Web 開發(fā)技術(shù)做了一個(gè)簡要的介紹 , OSGi 和 Web 的結(jié)合作為一種新型的開發(fā)方式值得研究的地方還很多。相信通過本文的示例項(xiàng)目,可以幫助您更快的掌握這種全新的開發(fā)方式并為以后的學(xué)習(xí)打下很好的基礎(chǔ)。
本文所有示例均在 Windows XP SP3 系統(tǒng)中測試完成。您需要一臺(tái)能流暢運(yùn)行 Windows XP 系統(tǒng)的機(jī)器,除此之外您還需要一些工具才能使用本教程中的代碼。所有這些工具都可以免費(fèi)下載:
JDK1.5 或更高版本
Eclipse 3.4 或更高版本
Tomcat 6.0 或更高版本
聯(lián)系客服