I am facing problems in an Jax-RS REST-Service when trying to update an entity containing enumerations via HTTP PUT.
I am putting the entity encoded as JSON, so the enumerations I am using in this JSON are delivered as strings.
I am getting following error:
Exception [EclipseLink-43] (Eclipse Persistence Services - 2.6.2.qualifier): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Missing class for indicator field value [AGENT] of type [class java.lang.String].
Descriptor: XMLDescriptor(de.org.app.business.account.Account --> [DatabaseTable(account)])
at org.eclipse.persistence.exceptions.DescriptorException.missingClassForIndicatorFieldValue(DescriptorException.java:940)
at org.eclipse.persistence.internal.oxm.QNameInheritancePolicy.classFromRow(QNameInheritancePolicy.java:278)
at org.eclipse.persistence.internal.oxm.TreeObjectBuilder.classFromRow(TreeObjectBuilder.java:182)
at org.eclipse.persistence.internal.oxm.TreeObjectBuilder.classFromRow(TreeObjectBuilder.java:1)
at org.eclipse.persistence.internal.oxm.record.UnmarshalRecordImpl.initializeRecord(UnmarshalRecordImpl.java:512)
at org.eclipse.persistence.internal.oxm.record.UnmarshalRecordImpl.startElement(UnmarshalRecordImpl.java:748)
at org.eclipse.persistence.internal.oxm.record.json.JsonStructureReader.parseRoot(JsonStructureReader.java:187)
at org.eclipse.persistence.internal.oxm.record.json.JsonStructureReader.parse(JsonStructureReader.java:140)
at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:978)
at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:425)
at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:375)
at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:708)
at org.eclipse.persistence.internal.oxm.XMLUnmarshaller.unmarshal(XMLUnmarshaller.java:643)
at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:339)
at org.eclipse.persistence.jaxb.rs.MOXyJsonProvider.readFrom(MOXyJsonProvider.java:668)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom(ReaderInterceptorExecutor.java:256)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:235)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:155)
at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundReadFrom(MappableExceptionWrapperInterceptor.java:74)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:155)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1085)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:874)
at org.glassfish.jersey.server.ContainerRequest.readEntity(ContainerRequest.java:271)
at org.glassfish.jersey.server.internal.inject.EntityParamValueFactoryProvider$EntityValueFactory.provide(EntityParamValueFactoryProvider.java:96)
at org.glassfish.jersey.server.spi.internal.ParamValueFactoryWithSource.provide(ParamValueFactoryWithSource.java:71)
at org.glassfish.jersey.server.spi.internal.ParameterValueHelper.getParameterValues(ParameterValueHelper.java:94)
at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$AbstractMethodParamInvoker.getParamValues(JavaResourceMethodDispatcherProvider.java:127)
at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$VoidOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:143)
at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102)
at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:326)
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:471)
at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:425)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:383)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:336)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:223)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:318)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:416)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:283)
at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:459)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:167)
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:283)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:200)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:132)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:111)
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:536)
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:591)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:571)
at java.lang.Thread.run(Thread.java:745)
Field Value AGENT is an enumeration in entity Account.
I only get this error in entities containing enumerations, in other cases the HTTP PUT works fine.
My guess is, as "AGENT" is delivered as string in JSON, it can't be resolved into an enum-value.
Is there any simple workaround I could go with?
Thanks for your help!
Edit for some more Code:
Okay, the enum is defined like this:
@XmlType(name = "type")
@XmlEnum
public enum AccountType implements TranslatableEnum {
@XmlEnumValue(value = "lov.account.type.customer")
CUSTOMER("lov.account.type.customer"),
@XmlEnumValue(value = "lov.account.type.agent")
AGENT("lov.account.type.agent")
//just getter/setter things following
}
The HTTP PUT in REST-Service like this:
@PUT
@Path(value = "{id}")
@Consumes(value = MediaType.APPLICATION_JSON)
public void edit(@PathParam(value = "id") final String id, final T entity) {
getRepository().update(entity);
}
with update-function:
public T update(@Observes @Update T entity) {
getEntityManager().merge(entity);
return entity;
}
any suggestions?
@XmlEnumValue
annotation declares which string value is mapped to annotated enum constant. i.e. when you write
@XmlEnumValue(value = "lov.account.type.customer")
CUSTOMER("lov.account.type.customer")
then CUSTOMER
constant is mapped to lov.account.type.customer
string.
So if you want that CUSTOMER
constant is mapped to CUSTOMER
string then you need to write
@XmlEnumValue(value = "CUSTOMER")
CUSTOMER("lov.account.type.customer")
or remove @XmlEnumValue
annotation i.e.
@XmlEnum
public enum AccountType implements TranslatableEnum {
CUSTOMER("lov.account.type.customer"),
AGENT("lov.account.type.agent")
//...
}
UPD
Now I see that root of the problem is inheritance. If you try to deserialize an inherited class EclipseLink by default uses @type field to determine which class should be instantiated for which input. See this answer. So you have several ways to go depending on your circumstances.
First way (dirty enough)
You can try to annotate your supertype with @XmlDiscriminatorNode("fakeField")
It is applicable only if you have one child class
Second way
If you have exact relation between AccountType
constant and child class you need to annotate each child class with @XmlDiscriminatorValue
annotation.
Example:
@XmlRootElement
public static class Parent {
AccountType type;
public AccountType getType() {
return type;
}
public void setType(AccountType type) {
this.type = type;
}
}
@XmlDiscriminatorValue("AGENT")
public static class Agent extends Parent {
}
@XmlDiscriminatorValue("CUSTOMER")
public static class Customer extends Parent {
}
It's not exactly the same scenario, but I had a similar problem in Jersey using JSON and Enums.
One thing I see in your enum definition is that you specify a custom name for the enum
AGENT("lov.account.type.agent")
But it seems you're sending AGENT in the JSON data. Shouldn't you be sending 'lov.account.type.agent' instead?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With