0

我正在开发一个 Osgi 包中的 REST API。我正在使用 Jersey 将服务部署到一个带有 javax servlet 的码头容器中,每个类都有 REST 服务。

每个类都有这样的属性

Private DBInterface dbInterface;

使用 setter 和 getter,一旦服务部署(在运行时),我需要从另一个包中注入对象。所以有人知道怎么做吗?

提前致谢。

PD:我想在不将服务声明为单例的情况下这样做,因此每个 REST 请求都会从服务的另一个实例(实际的无状态 REST)得到响应

更新:我用来部署服务的 web.xml 代码是:

<servlet>
    <servlet-name>jersey-serlvet</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
        <param-value>com.sun.jersey.api.core.ClassNamesResourceConfig</param-value>
    </init-param>
    <init-param>
        <param-name>com.sun.jersey.config.property.classnames</param-name>
        <param-value>com.mypackage.MyServiceClass</param-value>
    </init-param>
    <init-param>
        <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
        <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>jersey-serlvet</servlet-name>
    <url-pattern>/rest/*</url-pattern>
</servlet-mapping>
4

1 回答 1

0

首先要说的是,您应该在另一个捆绑激活器中将该对象注册为 OSGi 服务。在这种情况下,您几乎可以从任何地方访问它。

另一个问题是:如何从另一端访问它?有很多可能性,有些更好,有些更糟。

一个简单但不是很好的解决方案:

Bundle bundle = FrameworkUtil.getBundle(this.getClass());
// Getting the service and using it based on the bundle

这个解决方案的问题是,对于每个请求,您都必须根据过滤器从 OSGi 服务注册表获取 OSGi 服务,并在函数调用后取消获取它,这是不必要的开销。

基于ServiceTracker的解决方案:

如果您使用服务跟踪器,您将面临必须在某处打开并关闭它的问题。在构造函数中打开可能是一个解决方案,但是您不会在休息类中找到可以关闭它的位置。一个解决方案是您创建一个 Servlet 侦听器,在那里打开和关闭跟踪器,并将服务跟踪器放入 servlet 上下文属性映射中。在您的 REST 函数中,您可以访问 servlet 上下文并在每次调用中获取服务跟踪器,而不是调用 tracker.getService()。该函数将返回您需要的对象,如果尚未注册,则返回 null。

无需将 OSGi 相关代码放入 REST 类的解决方案:

您可能不想在 REST 代码中使用 ServiceTracker,因为您不想在那里依赖 OSGi。在这种情况下,您可以使用我实现的一个非常简单的库:)。它可在https://github.com/everit-org/osgi-servicereference获得。

方法论是一样的。您编写了一个侦听器: - 创建一个 ServiceReference - 您将 serviceReference 的 getProxtInstance 调用到 ServletContext - 您根据您在 REST 函数中提供的接口获取代理对象并调用其上的方法。

ServiceReference 是作为蓝图实现的一部分实现的。因此,它的工作方式与标记在蓝图 XML 文件中的工作方式相同。您可以实例化一个服务引用并打开它(打开后将跟踪 OSGi 服务)。您可以使用 getProxyInstance 方法获得一个实现必要接口(由您提供)的对象。

当基于您的接口对对象进行函数调用时:

  • 如果 OSGi 服务可用,则函数调用将委托给该服务
  • 如果没有可用的 OSGi 服务,则函数调用将一直保持到超时(简单的 thread.wait)。如果在超时之前存在 OSGi 服务,则将委托函数调用。如果没有,将抛出 ServiceUnavailableException。您可以通过为 ServiceReference 对象设置不同的超时处理程序来覆盖超时行为。

一个普通的函数调用是这样的:

调用者-> OSGiService.function

通过 ServiceReference 代理对象的函数调用如下所示:

调用者 -> ServiceReference.proxyObject -> OSGiService.function(如果 OSGiService 可用)

现在实践中的解决方案:

您应该为 Web 应用程序编写一个侦听器:

private Reference reference;

@Override
public void contextInitialized(final ServletContextEvent sce) {
    ServletContext servletContext = sce.getServletContext();
    BundleContext bundleContext = (BundleContext) servletContext.getAttribute("osgi-bundlecontext");

    try {
        Filter filter = bundleContext.createFilter("(objectClass=" + MyInterface.class.getName() + ")");

        // MyInterface is the interface the OSGi service implements (there can be multiple) and the timeout until function calls will wait is 5s.
        reference = new Reference(bundleContext, new Class<?>[] { MyInterface.class }, filter, 5000);

        // Opening the reference is necessary to track services
        reference.open();

        // Taking the proxy object into the servlet context attributes
        servletContext.setAttribute("myService", reference.getProxyInstance());
    } catch (InvalidSyntaxException e) {
        LOGGER.error(e.getMessage(), e);
    }
}

@Override
public void contextDestroyed(final ServletContextEvent sce) {
    if (reference != null) {
        reference.close();
        sce.getServletContext().removeAttribute("myService");
    }
}

拥有此侦听器后,您可以在代码中的任何位置(您可以访问 servlet 上下文)中的任何地方获取如下代理对象:

MyInterface myService = servletContext.getAttribute("myService");
myService.foo();
于 2013-08-21T11:46:32.970 回答