在部署的REST服务上注入对象

时间:2013-08-19 18:22:24

标签: rest servlets jersey osgi

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

每个类都有这样的属性

Private DBInterface dbInterface;

使用setter和getter,我需要在部署服务后(在运行时)从另一个bundle中注入对象。 所以任何人都知道这样做的方法吗?

提前致谢。

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>

1 个答案:

答案 0 :(得分:0)

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

另一个问题是:如何从另一方访问它?有许多可能性,其中一些更好,其中一些更糟糕。

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

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

根据您的界面对对象进行函数调用时:

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

正常的函数调用是这样的:

来电者 - &gt; OSGiService.function

通过ServiceReference Proxy对象进行的函数调用如下所示:

来电者 - &gt; ServiceReference.proxyObject - &gt; 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();