首页 文章

在OSGi环境中在Jetty中注册servlet(类加载问题)

提问于
浏览
3

我有一个带嵌入式Jetty 8.1.1服务器的OSGi应用程序 .

当我将我的应用程序作为monolit(没有OSGi环境)运行时,我可以通过调用 org.eclipse.jetty.servlet.ServletContextHandler.addServlet(String className, String mapping) 方法成功注册servlet . 所以现在我想在OSGi环境中做同样的事情 .

目标是编写一个bundle( servlet-extender ),它将在启动时注册其他bundle提供的servlet(扩展器模式) . 因此,在我的应用程序启动后,其中只有两个基本包运行: jettyservlet-extender .

1st attempt to write servlet-extender:

首先,我决定在提供servlet的包的 MANIFEST.MF 文件中声明servlet及其映射 . 如果启动此类包,则 servlet-extender 在其 MANIFEST.MF 中搜索servlet映射声明 . 如果找到任何servlet映射声明,则 servlet-extender 调用前面提到的 ServletContextHandler.addServlet(...) 方法来实际注册servlet .

虽然这个想法似乎没问题但是类加载存在问题 . 实际上Jetty称之为 Class.forName("org.my.servlets.MyServletClass").newInstance() . 虽然 jetty 包未导入 org.my.servlets 包,但调用 Class.forName("org.my.servlets.MyServletClass") 失败, ClassNotFoundException .

2nd attempt to write servlet-extender:

我搜索了一些与OSGi中的类加载相关的文章 . This one给了我一个希望,我可以通过一些OSGi服务提供加载的servlet类来解决上一个问题 . 所以我用 Map<Class<? extends Servlet>, String> getServlets() 方法创建了 ServletProvider service(该方法只返回映射到某些上下文的servlet类) . 然后我修改了 servlet-extender ,以便它不会在 MANIFEST.MF 中搜索servlet映射 . 它现在宁愿等到一些bundle注册 ServletProvider 实现 . 如果在OSGi服务注册表中注册了此类服务,则 servlet-extender 将调用其 getServlets() 方法并尝试在 jetty 中注册返回的servlet类 . 虽然 jetty 现在不需要调用 Class.forName("org.my.servlets.MyServletClass") ,但它仍然需要调用 servletClass.newInstance() 来实例化servlet . 不幸的是,它仍然以 ClassNotFoundException 失败了 .

我知道如果 jetty bundle会导入 org.my.servlets 包,这可以解决 . 但是这是不可能的,而 jetty 包是由第三方提供的,我无法修改其 Import-Package 声明 .

How to dynamically register servlets provided by arbitrary bundles?

P.S . :我不能使用“OSGi Http Service”,因为我必须使用Jetty的8.1.1 WebSocketServlet .

3 回答

  • 2

    问:我仍然对以声明方式注册servlet的方式感到好奇:通过servlet类名 . 可能吗?

    A:是的:)

    第一种可能的解决方案:如果允许修改jetty清单,可以在清单中添加“DynamicImport-Package:*”,这将允许它加载任何类,同时它不会依赖于自定义组织.my.servlets包 . 当然,请注意,这对于OSGi来说通常是不好的做法 .

    第二种可能的解决方案:码头仍然像它一样 . Servlet捆绑包列表,在清单中以声明方式包含servlet,与第一次尝试时一样 . 这里的关键是servlet扩展器 . 它将创建servlet并在jetty中注册它 . The important thing about this whole classloading problem is that the creation of the servlet object has to be done somewhere where the classloader (in this case the classloader of the servlet extender) has access to the classes of the servlets. 这不会发生在码头,因为你无法修改MF . 但是,您可以完全控制servlet扩展器的MF,对吧?

    您可以导入org.my.servlets(不推荐,因为无法预见将来必须安装哪些servlet);

    或者在MF中使用“DynamicImport-Package:*”(一般不推荐做法;))

    或者(推荐)扩展器使用servlet jar的类加载器从servlet jar加载类 - 使用 Bundle loadClass() 方法(参见org.osgi.framework.Bundle) . 这将正确加载类 . 请注意,这可能受到安全限制 .

  • 0

    你不必自己做 . 已经有pax-web提供了这个功能 . 见http://team.ops4j.org/wiki/display/paxweb/Pax+Web

    Pax web也可以在Apache Karaf中找到 . 在那里你可以安装它的功能:安装http-whiteboard .

    如果你想自己做,你可以看看pax web的代码 . 那里的概念是简单地将servlet注册为OSGi服务 . 使用服务属性,您可以提供诸如要注册的http路径之类的参数 .

  • 1

    我最近注意到Jetty的8.1.1 ServletContextHandler 有一个方法 addServlet(ServletHolder, String) . 我修改了前面提到的 servlet-extender 所以它现在以下列方式注册servlet:

    public void servletProviderRegistered(ServletProvider servletProvider) {
         Map<? extends HttpServlet, String> servlets = servletProvider.getServlets();
         for (Entry<? extends HttpServlet, String> entry : servlets.entrySet()) {
             HttpServlet servletInstance = entry.getKey();
             String mapping mapping = entry.getValue();
             ServletHolder servletHolder = new ServletHolder(servletInstance);
             servletContextHandler.addServlet(servletHolder, mapping);
         }
     }
    

    使用这种方法Jetty不需要调用 Class.forName("org.my.servlets.MyServletClass").newInstance() 所以现在我没有得到 ClassNotFoundException .

    因为这是我的问题的解决方案 I am still curious about the way of registering servlets declaratively: through servlet class name . Is it possible?

相关问题