• OSGI ServletBrige实现原理浅析


    这两天让我们在服务器上部署OSGI环境,应用服务器使用weblogic。结果由于对OSGI WEB知识的不了解,导致走了不少弯路。最后实在没办法,看了Felix Http Service的部分源代码,才最终将问题解决了。接下来做个笔记,分析一下Felix OSGI Servlet Brige的实现原理。

     
    目前实现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被获取和初始化的过程了。
     
  • 相关阅读:
    ASP.net Core
    Docker容器日志最佳实践
    手把手系列 搭建 efk 7 收集 docker 容器日志
    MyBatisPlus 字段为Null时不更新解决方案,MyBatisPlus 更新空字段
    外设驱动库开发笔记43:GPIO模拟SPI驱动
    Linux下安装Go环境
    升级openssl
    SpringBoot中try/catch异常并回滚事务(自动回滚/手动回滚/部分回滚)
    spring boot 3 集成websocket
    cocos3.x 中做渐出的效果
  • 原文地址:https://www.cnblogs.com/jdluojing/p/4201814.html
Copyright © 2020-2023  润新知