Consider a simple POJO called Biz:
public class Biz {
private String foo;
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
}
An instance of Biz, with request scope, is created in a CDI producer method:
@RequestScoped
public class BizProducer {
@Produces
@RequestScoped
public Biz produceBiz() {
return new Biz();
}
}
The Biz instance is injected into a JAX-RS resource class and then returned in the response:
@Path("/biz")
public class BizResource {
@Inject
private Biz biz;
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response get() {
return Response.ok(biz).build();
}
}
When returning a response with biz, I get the following exception:
Caused by: com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class org.jboss.weld.bean.StringBeanIdentifier and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: com.example.model.Biz$Proxy$_$$_WeldClientProxy["handler"]->org.jboss.weld.bean.proxy.ProxyMethodHandler["bean"]->org.jboss.weld.bean.ProducerField["identifier"])
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:59)
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:26)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:575)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:666)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:156)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:575)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:666)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:156)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:575)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:666)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:156)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:129)
at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:851)
at org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider.writeTo(ResteasyJackson2Provider.java:207)
at org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext.writeTo(AbstractWriterInterceptorContext.java:131)
at org.jboss.resteasy.core.interception.ServerWriterInterceptorContext.writeTo(ServerWriterInterceptorContext.java:60)
at org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext.proceed(AbstractWriterInterceptorContext.java:120)
at org.jboss.resteasy.security.doseta.DigitalSigningInterceptor.aroundWriteTo(DigitalSigningInterceptor.java:145)
at org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext.proceed(AbstractWriterInterceptorContext.java:124)
at org.jboss.resteasy.plugins.interceptors.encoding.GZIPEncodingInterceptor.aroundWriteTo(GZIPEncodingInterceptor.java:100)
at org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext.proceed(AbstractWriterInterceptorContext.java:124)
at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:98)
at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:466)
... 33 more
Looks like Jackson 2.8.2. is not getting well with CDI proxies.
If proxies are not valid beans, they would need specific support, possibly something like jackson-datatype-cdi. Core databind can not support this as classes are not part of core JDK.
As a workaround, I've remove the @RequestScoped annotation from the producer method:
@RequestScoped
public class BizProducer {
@Produces
public Biz produceBiz() {
return new Biz();
}
}
With this approach, the scope of Biz is _dependent_ (the Biz scope depends on the BizProducer scope). It works because CDI won't use proxies when injecting dependent scope beans.
It solved my issue, but I think it would be useful support serialization of CDI proxies.
Thank you for the work-around suggestion. I'll leave this open if anyone might have the itch to work on adding datatype module (or, whatever form the extension would take) to support this use case.
tnks very much!! it worker pretty well!
Just to add, i had to put the @Vetoed annotation on the Biz class.
Resurrecting this thread a bit, the hypothetical CDI datatype module really probably wouldn't have to be that complicated. Rather than, say, looking for Weld proxies specifically, a serializer could detect whether a given type is synthetic. If so, then it would simply walk the superclass hierarchy and stop at the first non-synthetic class: that's the "real" one to use. See this forum response for more details.
Hmmh. Question I have, wrt whether functionality could be in-built in databind, or separate module has to do with how generic logic would be. synthetic modifier is currently only used for skipping certain methods (bridge methods, I think, for co-variance).
But in this case I am not sure how things would work: original question seemed to be about type not exposing getters to use (or fields). Traversal of type hierarchy itself wouldn't seem to solve that issue would it? Jackson does check all super-types by default.
Maybe another way to put this is really that it'd be good to have a test case to show what should work.
I (or someone else with time and interest) could then have a look at what is missing and how things would work.
I also created a placeholder for CDI datatype, just in case:
Sounds good. FYI, "synthetic" here is simply a general-purpose marker at the _class_ level, not the (say) method level, so doesn't really have anything to do with bridge methods or anything like that. It's designed to mark classes that are generated not from source code on the fly for whatever reason, and the class writer can just flip this bit on. And that is, of course, exactly what a proxy class is, so it fits the bill nicely.
Indeed, synthetic classes—those generated out of thin air—are often used for bridge methods and the like, but they can be marked as synthetic for any purpose you want: the only requirement is that they didn't originate from source code.
I think it _could_ go in databind, but you'd want some way to enable it or turn it off for really really really weird edge cases. 99.9999% of the time it would be perfectly suitable to just check the synthetic bit.
What you would then _do_ as a result of finding that bit on is a little murkier: getting the superclass hierarchy is probably the best (and maybe the only?) general-purpose strategy, but I imagine there might be others.
Yes, I understood from context that it's class modifier. Only mentioned earlier usage as... well, not really relevant. But did not mean to imply there was anything specific other than indicator for something that was generated by tooling without explicit source code instruction.
But I think my main question really was this: I don't understand the reference to traversing class hierarchy. Jackson already checks super-classes and interfaces, including all annotations therein.
No special handling is associated with synthetic classes; but as long as such classes do not override any annotations (that is, have class/field/method annotations that mask equivalent annotations from supertypes) that does not matter.
Some other frameworks only pick annotations on direct classes, and in such cases I could see why one would want to "peel" off generated classes. But that's not problematic with Jackson.
Hmm; if it already walks the hierarchy to find state then yeah, I would think serialization would Just Workâ„¢.
I do see BeanPropertySerializer in the OP's stack trace; perhaps somebody somewhere does Introspector.getBeanInfo(someProxyClass), which would return an essentially "empty" BeanInfo and you'd have trouble at that point.
Right, if there are accessors there it could be some other complication. Test case reproduction would really be needed to see what is happening.
I think I assumed access would not be via getters, but some sort of "generic" accessor like public Object get(String propertyName), in which case special access code would be needed to introspect and traverse values. But it might just show how little I know about CDI :)