JAX-RS с Kotlin MessageBodyWriter не найден

Я реализовал ресурс JAX-RS (а) с Kotlin и (б) с Java. В то время как вариант Java работает, как и ожидалось, вариант Kotlin дает сбой с исключением MessageBodyWriter not found for media type=application/json, type=class java.util.ArrayList, genericType=java.util.List<? extends ....Entry>.|#].

это мой ресурс

@Path("demokotlin")
class DemoKotlinResource {
    @GET
    @Path("responseGenericEntityWithArrayList")
    fun arrayList(): Response {
        val list = arrayListOf(Entry("responseGenericEntityWithArrayList"))
        val entity = object : GenericEntity<List<Entry>>(list) {}
        return Response
                .status(Response.Status.OK)
                .entity(entity)
                .type(MediaType.APPLICATION_JSON).build()
    }
}

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
open class Entry(val fieldName: String)

Интеграционный тест по сути

private val client = ClientBuilder.newClient()
private val target = client.target("http://localhost:8080/xyz/resources/demokotlin")

@Test
fun responseGenericEntityWithArrayList() {
    val response = target.path("responseGenericEntityWithArrayList").request(MediaType.APPLICATION_JSON).get()
    assertThat("no ok status", response.status, equalTo(200))
    val entity = response.readEntity(JsonArray::class.java)
    assertThat(entity.getJsonObject(0).getString("fieldName"), equalTo("responseGenericEntityWithArrayList"))
    assertThat(entity.toString(), equalTo("[{\"fieldName\":\"responseGenericEntityWithArrayList\"}]"))
}

Эквивалентный код на Java работает нормально, в то время как показанный код на Kotlin выдает MessageBodyWriter not found for media type=application/json, type=class java.util.ArrayList, genericType=java.util.List<? extends ....Entry>.|#].

Я использую Payara 4.1.2.172. Копаясь в некоторых классах JAX-RS, я обнаружил, что код Java ведет к

genericType=java.util.List<....Entry>

(без ? extends), в то время как общая информация о типе с Kotlin

genericType=java.util.List<? extends ....Entry>

это может быть причиной? Если да, то как я могу это исправить?

edit 2017-07-25 Вот трассировка стека

#|2017-07-25T12:33:16.932+0000|INFO|Payara 4.1||_ThreadID=32;_ThreadName=http-thread-pool::http-listener-1(1);_TimeMillis=1500985996932;_LevelValue=800;|
javax.ws.rs.InternalServerErrorException: HTTP 500 Internal Server Error
        at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo(MappableExceptionWrapperInterceptor.java:90)
        at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:162)
        at org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.java:1130)
        at org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse(ServerRuntime.java:711)
        at org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(ServerRuntime.java:444)
        at org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:434)
        at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:329)
        at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
        at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
        at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
        at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)
        at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)
        at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:473)
        at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:427)
        at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:388)
        at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:341)
        at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:228)
        at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1606)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:338)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.glassfish.tyrus.servlet.TyrusServletFilter.doFilter(TyrusServletFilter.java:305)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:250)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:256)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:654)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:593)
        at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155)
        at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:371)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:238)
        at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:466)
        at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:169)
        at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:206)
        at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:180)
        at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:235)
        at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:284)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:201)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:133)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:112)
        at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
        at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:539)
        at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:112)
        at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:117)
        at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:56)
        at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:137)
        at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:593)
        at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:573)
        at java.lang.Thread.run(Thread.java:748)
Caused by: org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyWriter not found for media type=application/json, type=class java.util.ArrayList, genericType=java.util.List<? extends com.github.reproducer.boundary.Entry>.
        at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.java:247)
        at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:162)
        at org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor.aroundWriteTo(JsonWithPaddingInterceptor.java:106)
        at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:162)
        at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo(MappableExceptionWrapperInterceptor.java:86)
        ... 52 more
        |#]

редактирование № 2: рабочий код Java - должен быть очень похож на вариант Kotlin

@Path("demojava")
public class DemoJavaResource {

    @GET
    @Path("responseGenericEntityWithArrayList")
    public Response arrayList() {
        final List<JEntry> list = new ArrayList<>();
        list.add(new JEntry("responseGenericEntityWithArrayList"));
        final GenericEntity<List<JEntry>> entity = new GenericEntity<List<JEntry>>(list) {};
        return Response
            .status(Response.Status.OK)
            .entity(entity)
            .type(MediaType.APPLICATION_JSON).build();
    }
}

и JEntry

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
class JEntry {
    private String fieldName;

    JEntry() { this(""); }

    JEntry(final String responseGenericEntityWithArrayList) {
        fieldName = responseGenericEntityWithArrayList;
    }

    public String getFieldName() { return fieldName; }
}

person sk_dev    schedule 24.07.2017    source источник
comment
привет, пожалуйста, поместите трассировку стека в свой ответ.   -  person holi-java    schedule 24.07.2017


Ответы (1)


Поскольку JAX-RS не может кодировать ArrayList в массив JSON.

MessageBodyWriter — это обработчик для преобразования типа данных Java в выходной формат, например, XML, JSON. JAX-RS имеет встроенный MessageBodyWriter для основного типа данных (включая JAXB), однако он не включает ArrayList. Таким образом, вам нужно создать собственный MessageBodyWriter, чтобы принять ArrayList и закодировать в JSON.

Но в этом случае никакие кастомные MessageBodyWriter не нужны. Вместо этого вам нужно заново создать класс Entry. Мне жаль, что я не могу написать образец Kotlin, но если он пишет с использованием Java, он следующий;

@Path("demokotlin")
public class DemoKotlinResource {

    @GET
    @Path("responseGenericEntityWithArrayList")
    public Response arrayList() {
    Entry entity = new Entry();
        entity.fieldName.add("responseGenericEntityWithArrayList");
        return Response
                .status(Response.Status.OK)
                .entity(entity)
                .type(MediaType.APPLICATION_JSON).build();
    }
}

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Entry {
    public List<String> fieldName = new ArrayList<>();
}

И это перевести на Котлин, тогда, возможно, это будет завершено.

person HASUNUMA Kenji    schedule 27.07.2017
comment
Спасибо за ваш ответ. Как ни странно, мой Kotlin, переведенный на Java, работает (см. мое редактирование № 2 выше), поэтому мой MessageBodyWriter сериализует ArrayList, но, похоже, он придирчив к дженерикам. Я должен проверить ваше решение ... это не совсем то, чего я пытался добиться, но, возможно, все в порядке. - person sk_dev; 27.07.2017
comment
ваш код создает { "fieldName": [ "responseGenericEntityWithArrayList" ] } (он же список строк с меткой), мой код (Java) создает [ { "fieldName":"responseGenericEntityWithArrayList" } ] (список из fieldName -> String записей). Это не совсем то же самое. Поскольку мне нужно избегать изменений API, мне нужно найти решение Kotlin, похожее на мое решение Java. - person sk_dev; 31.07.2017