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

时间:2012-05-26 21:33:05

标签: servlets jetty osgi

我有一个带有嵌入式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

第一次尝试撰写servlet-extender

首先,我决定在提供servlet的bundle的MANIFEST.MF文件中声明servlet及其映射。如果启动了这样的bundle,那么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会失败。

第二次尝试撰写servlet-extender

我搜索了一些与OSGi中的类加载相关的文章。 This one让我希望通过一些OSGi服务提供加载的servlet类来解决之前的问题。所以我使用ServletProvider方法创建了Map<Class<? extends Servlet>, String> getServlets()服务(该方法只返回映射到某些上下文的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包将导入org.my.servlets包,则可以解决此问题。但是,如果jetty捆绑由第三方提供,我无法修改其Import-Package声明。

如何动态注册任意捆绑包提供的servlet?

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

3 个答案:

答案 0 :(得分:2)

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

答:是的:)

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

第二种可能的解决方案: 码头仍然像它一样。 Servlet捆绑包列表,在清单中以声明方式包含servlet,就像您第一次尝试一样。 这里的关键是servlet扩展器。它将创建servlet并在jetty中注册它。 关于整个类加载问题的重要一点是,servlet对象的创建必须在类加载器(在本例中是servlet扩展器的类加载器)可以访问servlet类的地方完成。 这在码头不可能发生,因为你无法修改MF。 但是,您可以完全控制servlet扩展器的MF,对吧?

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

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

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

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:0)

我最近注意到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

因为这是我的问题的解决方案我仍然对以声明方式注册servlet的方式感到好奇:通过servlet类名有可能吗?