Problem:
Given the REST endpoint:
@Path("/companies")
@Stateless
public class CompanyService{
@EJB
private CompanyEjb ejb;
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Company update(Company company){
return ejb.update(company);
}
}
After submitting a PUT
request, JAX-RS is expected to return the updated company such as
{
"id": 1,
"name": "Company",
"departments": [1,2]
}
However, only the primary key, 1
, is returned. A second request triggers an exception:
Warning: StandardWrapperValve[[RestApplicationClass]: Servlet.service() for servlet RestApplicationClass threw exception
java.lang.NoSuchMethodException: java.lang.Long.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.getDeclaredConstructor(Class.java:2178)
at org.eclipse.yasson.internal.ReflectionUtils.lambda$createNoArgConstructorInstance$1(ReflectionUtils.java:188)
at java.security.AccessController.doPrivileged(Native Method)
at org.eclipse.yasson.internal.ReflectionUtils.createNoArgConstructorInstance(ReflectionUtils.java:186)
at org.eclipse.yasson.internal.serializer.ObjectDeserializer.getInstance(ObjectDeserializer.java:92)
at org.eclipse.yasson.internal.serializer.AbstractContainerDeserializer.deserialize(AbstractContainerDeserializer.java:62)
at org.eclipse.yasson.internal.serializer.AdaptedObjectDeserializer.deserialize(AdaptedObjectDeserializer.java:94)
at org.eclipse.yasson.internal.Unmarshaller.deserializeItem(Unmarshaller.java:57)
at org.eclipse.yasson.internal.Unmarshaller.deserialize(Unmarshaller.java:50)
at org.eclipse.yasson.internal.JsonBinding.deserialize(JsonBinding.java:45)
at org.eclipse.yasson.internal.JsonBinding.fromJson(JsonBinding.java:85)
The adapter seems to be applied altough I don't have the company to be adapted
Context
Configuration is:
- Payara 5.0.0.Alpha3 (hence Yasson 1.0)
- Java EE 8 (hence JAX-RS 2.1, JSON-B 1.0 a JSON-P 1.1)
- Application is packed in EAR format
- REST request submitted with Postman
Two entities Company
and Department
are linked by a @OneToMany
relationships. Both entities have a primary key id
and a name name
. To avoid JSON infinite loop, the @ManyToOne
Company of Department has an adapter:
public class Department{
// ...
@ManyToOne // JPA stuff irrelevant here
@JsonbTypeAdapter(CompanyAdapter.class)
private Company company;
// ...
}
with the (simplified) adapter:
public class CompanyAdapter implements JsonbAdapter<Company, Long>{
@EJB
private CompanyEjb ejb;
@Override
public Long adaptToJson(Company orgnl) throws Exception {
return orgnl != null ? orgnl.getId() : null;
}
@Override
public E adaptFromJson(Long adptd) throws Exception {
return ejb.findById(adptd);
}
}
Workaround:
Initially, I thought that once an adapter is defined, they will be applied everywhere, similarly to JPA Converters with autoApply = true
. This led me nowhere
At first, I thought about using the last version of Yasson (1.0.2-SNAPSHOT) to fix the problem. To do so, I need to tell Payara to load the Yasson I want. So the glassfish-web.xml
is modified as followed:
<glassfish-web-app error-url="">
...
<!-- <class-loader delegate="true"/> -->
<class-loader delegate="false"/>
...
</glassfish-web-app>
according to Payara documentation.
But it appears than only switching the class-loader
delegation to false is enough.
=> why?
Subsidiary question:
Because of some hot-deploy issues, I need to do the following to test my changes:
- make the changes
- build & deploy
- restart the server after deploy
If by any chance someone knows why a hot-deploy/simple deploy modifies the JAX-RS adapter behaviour, I'm looking forward the answer