Jersey Web应用程序动态注册资源

时间:2016-09-26 07:37:39

标签: java web-services maven tomcat

我有一个Web应用程序,它将从jar文件中动态加载其资源。

我开始使用org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory.createHttpServer设置自己的服务器,并通过ServiceLoader加载所有资源,并通过ResourceConfig#register注册。这很好用,但我总是运行第二台服务器。

现在我想创建一个简单的应用程序之战并将其部署在例如tomcat的。这个想法是,当加载战争时,资源也通过ServiceLoader加载然后可用(与独立服务器相同)。

我如何重新编写加载和注册资源并启动服务器的主类,以便它在Web应用程序中运行?

我目前定义了javax.ws.rs.core.Application,在标有@PostConstruct的方法中加载资源。虽然这很有效,但每次请求都会初始化。

我尝试将javax.inject.Singleton添加到应用程序,但这导致了错误:

java.lang.IllegalStateException: Unable to perform operation: post construct on com.test.server.IntegrationServer
at org.jvnet.hk2.internal.ClazzCreator.create(ClazzCreator.java:392)
at org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:471)
at org.jvnet.hk2.internal.SingletonContext$1.compute(SingletonContext.java:83)
at org.jvnet.hk2.internal.SingletonContext$1.compute(SingletonContext.java:71)
at org.glassfish.hk2.utilities.cache.Cache$OriginThreadAwareFuture$1.call(Cache.java:97)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at org.glassfish.hk2.utilities.cache.Cache$OriginThreadAwareFuture.run(Cache.java:154)
at org.glassfish.hk2.utilities.cache.Cache.compute(Cache.java:199)
at org.jvnet.hk2.internal.SingletonContext.findOrCreate(SingletonContext.java:122)
at org.jvnet.hk2.internal.Utilities.createService(Utilities.java:2020)
at org.jvnet.hk2.internal.ServiceLocatorImpl.internalGetService(ServiceLocatorImpl.java:766)
at org.jvnet.hk2.internal.ServiceLocatorImpl.getService(ServiceLocatorImpl.java:705)
at org.glassfish.jersey.server.ApplicationHandler.createApplication(ApplicationHandler.java:385)
at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:342)
at org.glassfish.jersey.servlet.WebComponent.<init>(WebComponent.java:392)
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:177)
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:369)
at javax.servlet.GenericServlet.init(GenericServlet.java:158)

我的做法完全错了吗?或者我如何创建这样一个系统,作为可部署的战争运行?

*编辑*

使用ServletContextListener,我可以解决多个init调用,但是我的资源没有被加载。

所以,我有这样的资源

@Path("test")

公共类TestResource实现MarkerInterface {     @得到     @Produces(MediaType.TEXT_PLAIN)     @Path( “获取”)     public String get(){         返回“你好”;     } } 此资源打包在一个jar文件中,位于文件系统中。

应用程序现在加载此文件位置并通过ServiceLoader所有MarkerInterface类加载并注册它们。

我有

@WebListener
public class IntegrationServer extends ResourceConfig implements ServletContextListener {

@Override
public void contextInitialized(ServletContextEvent sce) {
    LOGGER.info(() -> "CONTEXT INIT");
    try {
        register(JacksonFeature.class);
        // wrapper for the ServiceLoader implementation
        this.serviceLoader = new IntegrationServiceLoader();
       this.serviceLoader.loadIntegrations(MarkerInterface.class, gce -> {
            LOGGER.info(() -> "Adding " + gce.getClass() + " to Server");
            register(gce.getClass());
        });
    }
    catch (IOException e) {
        LOGGER.log(Level.SEVERE, e, () -> "Failed to init integration server");
    }
}

}

我在tomcat上将注册输出部署为server.war时会看到日志中的注册输出,但是当我调用http://localhost:8080/server/test/get时,我收到HTTP状态404 - 未找到。

我的web.xml看起来像

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<servlet>
    <servlet-name>Jersey Web Application</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>Jersey Web Application</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

**编辑2 ***

请参阅CássioMazzochiMolin对此讨论的回答。

我使用构建器注册我的资源并将其打印出来

Builder{names=[com.test.server.TestResource], path='backend', methodBuilders=[], childResourceBuilders=[Builder{names=[[unnamed]], path='info', methodBuilders=[org.glassfish.jersey.server.model.ResourceMethod$Builder@819f71f], childResourceBuilders=[], childResources=[], resourceMethods=[], subResourceLocator=null, handlerClasses=[], handlerInstances=[], parentResource=Builder{names=[com.cetrea.qa.server.TestResource], path='backend'}, extended=false}], childResources=[Resource{"achievements", 3 child resources, 0 resource methods, 0 sub-resource locator, 1 method handler classes, 0 method handler instances},

这是其中的一部分。 TestResource有一个方法info,因此我可以调用http://localhost:8080/server/backend/info并获得正确的结果。 资源achievements来自插件,例如一个all方法,所以我尝试http://localhost:8080/server/backend/achievements/all(也没有backend部分),但这再次给出了404

3 个答案:

答案 0 :(得分:2)

Jersey提供了一个用于以编程方式构建资源的API。

根据documentationResource类是程序化资源建模API的主要入口点,它提供了以编程方式扩展现有JAX-RS带注释资源类或构建新资源模型的能力。可以由Jersey运行时使用。

查看文档提供的示例:

@Path("hello")
public class HelloResource {

     @GET
     @Produces("text/plain")
     public String sayHello() {
         return "Hello!";
     }
}
// Register the annotated resource.
ResourceConfig resourceConfig = new ResourceConfig(HelloResource.class);

// Add new "hello2" resource using the annotated resource class
// and overriding the resource path.
Resource.Builder resourceBuilder =
        Resource.builder(HelloResource.class, new LinkedList<ResourceModelIssue>())
        .path("hello2");

// Add a new (virtual) sub-resource method to the "hello2" resource.
resourceBuilder.addChildResource("world").addMethod("GET")
        .produces("text/plain")
        .handledBy(new Inflector<Request, String>() {
                @Override
                public String apply(Request request) {
                    return "Hello World!";
                }
        });

// Register the new programmatic resource in the application's configuration.
resourceConfig.registerResources(resourceBuilder.build());

下表说明了上述示例中配置的应用程序支持的请求和提供的响应:

 Request              |  Response        |  Method invoked
----------------------+------------------+--------------------------
 GET /hello           | "Hello!"         | HelloResource.sayHello()
 GET /hello2          | "Hello!"         | HelloResource.sayHello()
 GET /hello2/world    | "Hello World!"   | Inflector.apply()

有关其他详细信息,请查看Jersey documentation

答案 1 :(得分:1)

尝试使用ServletContextListener。这应该为你提供一个做同样工作的钩子。

答案 2 :(得分:0)

我在最后几个小时内一直在寻找这个。在适当尊重解决方案的建议和其他问题的同一问题区域是半熟。大多数解决方案都以编程方式说明了扩展REST资源的代码。我缺少的是我在哪里放这个代码。

Jersey文档给出了正确的方法。以下是链接:

https://jersey.github.io/documentation/latest/resource-builder.html#d0e10860

诀窍是创建自己的自定义CustomResourceConfig类,扩展标准球衣ResourceConfig类。然后将CustomResourceConfig类作为init参数传递给Jersey servlet。

<servlet>
<servlet-name>org.glassfish.jersey.examples.helloworld.MyApplication</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
    <param-name>javax.ws.rs.Application</param-name>
    <param-value>org.glassfish.jersey.examples.helloworld.MyResourceConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>

请遵循上面给出的球衣文件,我相信一切顺利。

希望这有帮助