OSGI ServletBrige實現原理淺析

這兩天讓咱們在服務器上部署OSGI環境,應用服務器使用weblogic。結果因爲對OSGI WEB知識的不瞭解,致使走了很多彎路。最後實在沒辦法,看了Felix Http Service的部分源代碼,才最終將問題解決了。接下來作個筆記,分析一下Felix OSGI Servlet Brige的實現原理。web

 
目前實現OSGI WEB應用主要有兩種實現,一種是將webServer嵌入到OSGI環境中,另外一種是將OSGI嵌入到webServer中。目前第一種方式只有jetty支持得比較好,而第二種目前尚未相關的webServer可以很好的支持。
 
另一種實現方式就是使用ServletBrige來創建webServer和OSGI之間的鏈接。目前知道的ServletBrige有felix和equinox,後者聽說許久未更新,而且效率也不高。
 
接下來就來分析一下Felix的ServletBrige的實現。首先ServletBrige最主要的工做就是要將webServer和OSGI連接起來。從代碼角度來看就是要將ServletContext和OSGI BundleContext關聯起來,使得當http請求到達webServer的時候,webServer可以將請求傳遞給OSGI。而ServletBrige在實現OSGI HttpService服務的時候,還須要用到ServletContext。
上圖是Felix ServletBrige的大體實現原理圖。
 
解析來進入代碼分析,結合官方給的一個實例來分析。首先看web.xml中的配置:
    <listener>
        <listener- class>org.apache.felix.http.samples.bridge.StartupListener</listener- class>
    </listener>

    <servlet>
        <servlet- name>proxy</servlet- name>
        <servlet- class>org.apache.felix.http.proxy.ProxyServlet</servlet- class>
        <load-on-startup> 1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet- name>proxy</servlet- name>
        < url-pattern>/*</ url-pattern>
    </servlet-mapping>
 
首先看到註冊了一個ServletContextListener,而後註冊了一個ProxyServlet,該Servlet的pattern爲 /* ,也就是說全部請求都由該Servlet處理。結合上面的原理圖,大體就能知道這這些配置的做用了。
首先看StartupListener的源碼:
  public void contextInitialized(ServletContextEvent event){
        this.service = new FrameworkService(event.getServletContext());
        this.service.start();
}
這裏調用了FrameworkService的start方法,內部又調用了doStart方法:
private void doStart() throws Exception{
    Felix tmp = new Felix(createConfig());
    tmp.start();
    this.felix = tmp;
}
這裏調用了createConfig方法來生成配置信息,接着啓動了Felix:
private Map <String, Object > createConfig() throws Exception{
    Properties props = new Properties();
    props.load( this.context.getResourceAsStream( "/WEB-INF/framework.properties"));

    HashMap <String, Object > map = new HashMap <String, Object >();
    for (Object key : props.keySet()) {
        map.put(key.toString(), props.get(key));
    }   
    map.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP, Arrays.asList(new ProvisionActivator(this.context)));
    return map;
}
這裏配置信息中有個比較重要的信息,紅色代碼部分設置了felix.systembundle.activators屬性,這個屬性的做用就是在框架的系統bundle都啓動時會調用配置的BundleActivator。接着看ProvisionActivator類的start方法:
public void start(BundleContext context) throws Exception{
    servletContext.setAttribute(BundleContext.class.getName(), context);
    ArrayList <Bundle > installed = new ArrayList <Bundle >();
    for (URL url : findBundles()) {
        this.servletContext.log( "Installing bundle [" + url + "]");
        Bundle bundle = context.installBundle(url.toExternalForm());
        installed.add(bundle);
    }

    for (Bundle bundle : installed) {
        bundle.start();
    }
}
經過紅色標註的代碼,能夠看到,這裏將OSGI的BundleContext設置爲了ServletContext的屬性(以便後續使用),而後從指定的目錄下獲取了bundle(jar)而且安裝到OSGI Framework中,而後啓動這些bundle。
 
到這裏,OSGI框架就啓動了,而且WebServer也成功的拿到了BundleContext。接下來分析ServletBrige內部的實現了。使用Felix的ServletBrige時須要將org.apache.felix.http.bridge.jar放置到OSGI環境中,也就是說這個Bundle至關重要。首先看它的BundleActivator:
public final class BridgeActivator extends AbstractHttpActivator{
  protected void doStart() throws Exception {
    super.doStart();
    Hashtable <String, Object > props = new Hashtable();
    props.put( "http.felix.dispatcher", getDispatcherServlet().getClass().getName());
    getBundleContext().registerService(HttpServlet.class.getName(), getDispatcherServlet(), props);
  }
}
這裏紅色標註的兩行代碼至關重要,調用父類進行了一些必要的設置,而後發佈了一個服務(Servlet)。
看父類AbstractHttpActivator的dostart方法:
protected void doStart() throws Exception{
    controller = new HttpServiceController(getBundleContext());
    dispatcher = new DispatcherServlet(controller);
}
這裏只是實例話了兩個對象,後一個是一個Servlet,而且用到了前面的controller.而且這個dispatcher就是上面一步要發佈出去的Service。咱們首先來看這個Servlet:
public void init(ServletConfig config) throws ServletException{
    super.init(config);
    controller.register(getServletContext());
}
  
public void destroy(){
    controller.unregister();
    super.destroy();
}
  
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException{
    controller.getDispatcher().dispatch(req, res);
}
這裏能夠看到,內部對controller作了很多處理。其中的請求處理也是調用了controller來處理的。查看controller的register方法:
public void register(ServletContext servletContext){
    HttpServiceFactory factory = new HttpServiceFactory(servletContext, registry);
    String[] ifaces = { HttpService. class.getName(), ExtHttpService. class.getName() };
    serviceReg = bundleContext.registerService(ifaces, factory, serviceProps);
}
在這個方法裏面,向OSGI註冊了HttpService服務,使用的是本身的實現。那麼到這裏OSGI中註冊的Servlet都被收集到了。這裏我就不去分析HttpService的實現了。
 
分析到這裏,ServletContext中持有了BundleContext,OSGI框架中也有了HttpService服務。OSGI也發佈了DispatcherServlet服務,而且它的service方法調用了controller來將http請求分發到OSGI中註冊的Servlet上。
 
還須要作的就是將WevServer中的請求,交付到OSGI發佈的這個DispacherServlet手上來。這個工做就是由web.xml中註冊的ProxyServlet來完成的:
public void init(ServletConfig config) throws ServletException{
    super.init(config);
    //省略異常捕獲代碼 
    doInit();
}
private void doInit() throws Exception{
    tracker = new DispatcherTracker(getBundleContext(), null, getServletConfig());
    tracker.open();
}
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException{
    HttpServlet dispatcher = tracker.getDispatcher();
    if (dispatcher != null) {
      dispatcher.service(req, res);
    } else {
      res.sendError( 503);
    }

public void destroy(){
    tracker.close();
    super.destroy();
}
這裏使用了一個OSGI的ServiceTracer來引用以前發佈的DispacherServlet服務,而且在處理請求時調用了DispatcherServlet的service方法,那麼請求就從ProxyServlet傳遞到了ServletBrige中的DispacherServlet了,後續的處理就明瞭了。
 
這裏還有個問題,Brigade中雖然發佈了DispacherServlet服務,可是並無初始化它。若是要獲取到WebServer的ServletContext,那麼這個Servlet就必須拿到容器中的ServletConfig。也就是說ProxyServlet在獲取到DispacherServlet服務的實例後,還須要先對他進行初始化,而後才能使用。
上面代碼中紅色部分第一行,實例化了一個DispacherTracker,參數中有BundleContext,還有 ServletConfig:
public DispatcherTracker(BundleContext context, String filter, ServletConfig config) throws Exception{
    super(context, createFilter(context, filter), null);
    this.config = config;
}
由於這個類是ServiceTracker的子類,當有符合條件的Service可用時,會調用ServiceTracker的addService方法:
public Object addingService(ServiceReference ref){
    Object service = super.addingService(ref);
    if ((service instanceof HttpServlet)) {
      setDispatcher((HttpServlet)service);
    }
    return service;
}
private void setDispatcher(HttpServlet dispatcher){
    destroyDispatcher();
    this.dispatcher = dispatcher;
    initDispatcher();
}
private void initDispatcher(){
    if (dispatcher == null) {
      return;
    }
    
    dispatcher.init(config);
    
}
 
這段代碼就能夠看到DispacherServlet被獲取和初始化的過程了。
相關文章
相關標籤/搜索