3

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

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

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

第一次尝试写servlet-extender

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

虽然这个想法看起来不错,但类加载存在问题。事实上 Jetty 调用Class.forName("org.my.servlets.MyServletClass").newInstance(). 虽然jettybundle 不导入org.my.servlets包,但调用Class.forName("org.my.servlets.MyServletClass")失败并显示ClassNotFoundException.

第二次尝试写servlet-extender

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

jetty我知道如果捆绑包会导入包,这可以解决org.my.servlets。但这是不可能的,因为jettybundle 是由 3rd 方提供的,我不能修改它的Import-Package声明。

如何动态注册任意bundle提供的servlet?

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

4

3 回答 3

2

Q:我还是对声明式注册servlet的方式很好奇:通过servlet类名。可能吗?

答:是的:)

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

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

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

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

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

于 2012-05-30T10:09:41.983 回答
1

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

Pax web 也可​​以在 Apache Karaf 中使用。在那里您可以使用功能安装它:安装 http-whiteboard。

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

于 2012-05-27T12:03:44.677 回答
0

I recently noticed that Jetty's 8.1.1 ServletContextHandler has a method addServlet(ServletHolder, String). I modified aforementioned servlet-extender so it now registers servlets in the following way:

 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);
     }
 }

Using this approach Jetty doesn't need to call Class.forName("org.my.servlets.MyServletClass").newInstance() so now I don't get ClassNotFoundException.

Since this is a solution to my problem I am still curious about the way of registering servlets declaratively: through servlet class name. Is it possible?

于 2012-05-28T12:57:46.497 回答