Jersey测试客户端不使用JSON正文

时间:2014-02-12 21:10:40

标签: java jersey jax-rs jersey-2.0 jersey-client

我为JSON编写了一个自定义消息体编写器和读取器,而不是与Jersey打包的库。我试图用简单的Java服务和测试类来测试它。我遇到了一些问题,因为测试客户端因为找不到原因而永远不会看到JSON主体。启用日志记录工具后,我发现服务器似乎正确发送了JSON数据:

1:46:03 PM com.sun.jersey.api.container.filter.LoggingFilter$Adapter finish
INFO: 1 * Server out-bound response
1 < 200
1 < Content-Type: application/json
1 < 
{"id":1,"name":"Business Intelligence Team","parentTeam":{"id":2,"name":"Data Analysis Team"}}

但客户方从未收到这样的身体:

1:46:03 PM com.sun.jersey.api.client.filter.LoggingFilter log
INFO: 1 * Client in-bound response
1 < 200
1 < Content-Length: 0
1 < Date: Wed, 12 Feb 2014 18:46:03 GMT
1 < server: grizzly/2.2.16
1 < Content-Type: application/json
1 < 

调试Jersey库,我看到确实,InputStream自动设置在默认的EmptyInputStream对象上,好像这从未正确设置,或者至少不承认任何输入http体。调试我的自定义提供程序,它们似乎运行良好并且行为正确。

我使用泽西岛版本1.18。这是我的测试类:

public class DeployResourceTest extends JerseyTest {
    private WebResource resource;

    @BeforeClass
    public static void setupJerseyLog() throws Exception {
        Handler fh = new ConsoleHandler(); // FileHandler("/tmp/jersey_test.log");
        Logger.getLogger("").addHandler(fh);
        Logger.getLogger("com.sun.jersey").setLevel(Level.FINEST);
    }

    public DeployResourceTest() {
        super(new WebAppDescriptor.Builder("com.deploymentnow.dnagent.services.rest")
                .contextParam("contextConfigLocation", "classpath:applicationContext.xml")
                .contextParam("log4jConfigLocation", "src/main/webapp/WEB-INF/log4j.properties")
                .contextParam("webAppRootKey", "template-jersey-spring-jpa.root")
                .initParam("com.sun.jersey.config.property.packages", "com.deploymentnow.agent.services.rest")
                .initParam("com.sun.jersey.config.feature.Trace", "true")
                .initParam("jersey.config.server.provider.classnames", "org.glassfish.jersey.filter.LoggingFilter")
                .initParam("com.sun.jersey.spi.container.ContainerRequestFilters", "com.sun.jersey.api.container.filter.LoggingFilter")
                .initParam("com.sun.jersey.spi.container.ContainerResponseFilters", "com.sun.jersey.api.container.filter.LoggingFilter")
                .clientConfig(new DefaultClientConfig(PlayJsonMessageBodyReader.class, PlayJsonMessageBodyWriter.class))
                .contextListenerClass(Log4jConfigListener.class)
                .contextListenerClass(ContextLoaderListener.class)
                .requestListenerClass(RequestContextListener.class)
                .servletClass(SpringServlet.class).build());
        this.resource = resource();
        resource.addFilter(new LoggingFilter());
    }

    @Override
    protected TestContainerFactory getTestContainerFactory() {
        return new GrizzlyWebTestContainerFactory();
    }

    @Test
    public void testListResources() {
        ClientResponse response = resource.path("application/26/team").accept(MediaType.APPLICATION_JSON_TYPE).get(ClientResponse.class);
        Team team = response.getEntity(Team.class);
        Assert.assertNotNull(team);
    }

}

我的网络服务,非常小:

@Path("application/{application}")
public class DeployResource {

    @Path("team")
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response testTeam() {
        Team team = new Team(1, "Business Intelligence Team", Option.<Team>apply(new Team(2, "Data Analysis Team", Option.<Team>empty())));
        return Response.status(200).entity(team).build();
    }

}

邮件正文撰稿人。它目前实现了一种非常特定的类型,因为它仅用于测试:

@Provider
@Produces("application/json")
public class PlayJsonMessageBodyWriter implements MessageBodyWriter<Team> {
    @Override
    public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return type == Team.class && MediaType.APPLICATION_JSON_TYPE.equals(mediaType);
    }

    @Override
    public long getSize(Team o, Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        // deprecated by JAX-RS 2.0 and ignored by Jersey runtime
        return 0;
    }

    @Override
    public void writeTo(Team team, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
        JsValue value = Json.toJson(team, DeploymentFormat.teamFormat());
        String str = value.toString();
        byte[] bytes = str.getBytes(Charset.forName("UTF-8"));
        entityStream.write(bytes);
    }
}

我正在直接在实体流中编写,如文档和教程所示..以及消息正文阅读器:

@Provider
@Consumes("application/json")
public class PlayJsonMessageBodyReader implements MessageBodyReader<Team> {

    @Override
    public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return type == Team.class && MediaType.APPLICATION_JSON_TYPE.equals(mediaType);
    }

    @Override
    public Team readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
        String body = IOUtils.toString(entityStream, "UTF-8");
        if (!hasText(body)) {
            return null;
        }
        JsValue json = Json.parse(body);
        JsResult<Team> result = Json.fromJson(json, DeploymentFormat.teamFormat());
        return result.get();
    }
}

我还尝试使用REST客户端查询部署在Tomcat servlet容器中的Web服务,但我没有响应,尽管日志记录似乎另有说明。我想知道日志记录是否以某种方式消耗了内容体..但即使禁用日志记录,它也无法正常工作。


更新

我能够使我的工作正常,但不是通过直接解决我的问题,而是通过将JAX-RS实现从 Jersey 切换到 Apache CXF 。代码没有区别;它只是有效。

我看到的主要区别是使用的输出流类型。在我的提供程序中,Jersey向我发送了一些CommittingOutputStream参数,它似乎具有更多的抽象性和一点点敏感性。例如,我注意到在其上调用flush方法会使流的内容完全消失,甚至无法出现在日志记录中。

对于Apache CXF,输出流类型为CachedOutputStream。它的行为与预期的一样,内容实际上是发送给客户端的。

我还没有找到为什么它在 Jersey 中不起作用。只需转储它并获得完成工作的东西就更快了,a.k.a。 Apache CXF

如果泽西队的某个人能够回答这种不稳定的行为,我会打开这个问题。

0 个答案:

没有答案