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被获取和初始化的过程了。
相关文章
相关标签/搜索