1.目的
在这篇文章中,我们将采取两种方式用OSGI运行Servlet,一种是利用服务追踪器的方式,一种是利用声明式服务的方式。
2.概念
在OSGI中,最重要的概念就是Bundle、服务和生命周期。
Bundle也就是OSGI模块化编程的模块;Bundle之间的互相依赖主要体现在,某些Bundle会注册服务提供使用,而某些Bundle则会使用这些服务;而生命周期,则是指在运行系统时,可以安装、启动、停止、更新和卸载Bundle。
由于OSGI系统本身是松耦合的,所以并不存在一个主Bundle最先运行的概念,所有Bundle都是同级运行的。这样就引发一个问题,即如果Bundle A引用了Bundle B的服务,那么如果Bundle A在Bundle B之前运行,就会出错。
服务追踪器和声明式服务则让我们可以规避掉Bundle启动顺序的问题,它使得我们可以在所依赖的服务被注册时才使用该服务(这点是OSGI内部已经实现好了的,我们只需要按照它提供的API使用即可)。
3.实现
1.服务追踪器方式运行Servlet
a.首先,新建一个plugin project,命名为 org.osgiequinox.servlet.test1。
b.然后,选择Run-Run Configuration,选中OSGI Framework新建一个New_Configuration。在Bundle页签中,首先选择Deselect All,然后将org.osgiequinox.servlet.test1勾选上,同时还需要勾选上运行OSGI项目所需要的其他Bundle,分别为:
所有含有"jetty"字样的bundle
org.apache.felix.gogo.command
org.apache.felix.gogo.runtime
org.apache.felix.gogo.shell
org.eclipse.equinox.console
org.eclispe.quinox.util
org.eclipse.osgi.services
再选择“Add Required Bundle”添加所有其他依赖的Bundle。
此时,选择run。在浏览器中输入http://localhost,可以看到:
Problem accessing /. Reason:
ProxyServlet: /Powered by Jetty://
c.我们选中MANIFEST.MF,然后选择Dependencies,然后选择Auto Management of Dependencies,保证Imported-Package被勾选上。加入以下包。
javax.servlet
javax.servlet.http
org.osgi.framework
org.osgi.util.tracker
org.osgi.service.http
d.我们在src/org.osgiequinox.servlet.test1下新建TestServlet,代码如下:
package org.osgiequinox.servlet.test1; import java.io.PrintWriter; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author Tammy Pi * @function print a sentence */ public class TestServlet extends HttpServlet{ public void doGet(HttpServletRequest request,HttpServletResponse response){ response.setContentType("text/html"); PrintWriter out = null; try{ out = response.getWriter(); out.println("Hello OSGI!"); out.flush(); }catch(Exception ex){ ex.printStackTrace(); }finally{ if(out != null){ out.close(); } } } }
package org.osgiequinox.servlet.test1; import javax.servlet.ServletException; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.service.http.HttpService; import org.osgi.service.http.NamespaceException; import org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTrackerCustomizer; /** * @author Tammy Pi * @function 1.tracker httpservice 2.register servlet */ public class Activator implements BundleActivator { private ServiceTracker httpServiceTracker; private BundleContext ctx; private HttpService httpService; private ServiceTrackerCustomizer createHttpServiceCustomizer(){ return new ServiceTrackerCustomizer(){ @Override public Object addingService(ServiceReference arg0) { Object service = ctx.getService(arg0); Activator.this.httpService = (HttpService) service; try { Activator.this.httpService.registerServlet("/demo/test",new TestServlet(),null,null); } catch (ServletException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NamespaceException e) { // TODO Auto-generated catch block e.printStackTrace(); } return service; } @Override public void modifiedService(ServiceReference arg0, Object arg1) { } @Override public void removedService(ServiceReference arg0, Object arg1) { if(arg1 != Activator.this.httpService){ return; } Activator.this.httpService.unregister("/demo/test"); Activator.this.httpService = null; } }; } @Override public void start(BundleContext arg0) throws Exception { ctx = arg0; ServiceTrackerCustomizer cus = createHttpServiceCustomizer(); httpServiceTracker = new ServiceTracker(arg0,HttpService.class.getName(), cus); httpServiceTracker.open(); } @Override public void stop(BundleContext arg0) throws Exception { httpServiceTracker.close(); } }
即可以看到:
Hello OSGI!
字样,即表明运行servlet成功。
2.声明式服务方式运行Servlet
从上面可以看到,通过服务追踪器运行servlet需要用到ServiceTracker和ServiceCustomerizer,有点小复杂(考虑一下需要用到很多服务的情况)。有没有一种办法可以让我们不用写那么多代码,服务就自动依赖注入进去呢?就像Spring的IoC那样。这个时候,就是声明式服务发挥作用的时候了。
a.咱们换一个workspace,然后再新建一个Plugin Project,取名为org.osgiequinox.servlet.test2
b..然后,选择Run-Run Configuration,选中OSGI Framework新建一个New_Configuration。在Bundle页签中,首先选择Deselect All,然后将org.osgiequinox.servlet.test2勾选上,同时还需要勾选上运行OSGI项目所需要的其他Bundle,分别为:
所有含有"jetty"字样的bundle
org.apache.felix.gogo.command
org.apache.felix.gogo.runtime
org.apache.felix.gogo.shell
org.eclipse.equinox.console
org.eclispe.quinox.util
org.eclipse.osgi.services
org.eclipse.equinox.ds
再选择“Add Required Bundle”添加所有其他依赖的Bundle。
c.由于声明式服务不需要激活器了,所以我们把项目中的激活器给删掉
d.我们选中MANIFEST.MF,然后选择Dependencies,然后选择Auto Management of Dependencies,保证Imported-Package被勾选上。加入以下包。
javax.servlet
javax.servlet.http
org.osgi.framework
org.osgi.service.httpe.在src/org.osgiequinox.servlet.test2目录下,新建TestServlet,代码如下:
package org.osgiequinox.servlet.test2; import java.io.PrintWriter; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author Tammy Pi * @function print a sentence */ public class TestServlet extends HttpServlet{ public void doGet(HttpServletRequest request,HttpServletResponse response){ response.setContentType("text/html"); PrintWriter out = null; try{ out = response.getWriter(); out.println("Hello OSGI again!"); out.flush(); }catch(Exception ex){ ex.printStackTrace(); }finally{ if(out != null){ out.close(); } } } }f.在同级目录下新建Component.java文件,内容如下:
package org.osgiequinox.servlet.test2; import javax.servlet.ServletException; import org.osgi.service.http.HttpService; import org.osgi.service.http.NamespaceException; /** * @author Tammy Pi * @function register servlet */ public class Component { private HttpService httpService; public void setHttpService(HttpService httpService) { this.httpService = httpService; } public void startup(){ try { this.httpService.registerServlet("/demo/test", new TestServlet(), null, null); } catch (ServletException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NamespaceException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void shutdown(){ this.httpService.unregister("/demo/test"); } }
g.在项目上点击右键,New->Other->Plugin Devlopment->Component Definition。然后选择文件夹OSGI-INF,类org.osgiequinox.servlet.test2.Component,新建component.xml文件,文件内容如下:
<?xml version="1.0" encoding="UTF-8"?> <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.osgiequinox.servlet.test2" activate="startup" deactivate="shutdown"> <implementation class="org.osgiequinox.servlet.test2.Component"/> <reference bind="setHttpService" name="httpService" interface="org.osgi.service.http.HttpService" policy="static"/> </scr:component>
可以看到:Hello OSGI again!
声明式服务运行servlet成功。
4.总结
以上就是通过服务追踪器和声明式服务运行Servlet的过程,可以看到,声明式服务更加简洁、明了。