How to use custom jackson de-/serializer in kie execution server? - jackson

I am using Kie Execution Server 6.5 (docker image). I deploy a kie container (kjar) which contains some rules and some custom java classes. Everything was working so far.
Now, I added some 3rd party classes to my java classes (geojson-jackson
), which use a custom jackson serializer and deserializer declared with #JsonDeserialize(using = LngLatAltDeserializer.class), see LngLatAlt.
First I had class loading issues, apparently because of different jackson versions, which I solved by using geojson-jackson 1.3 and excluding the jackson dependency in my pom.xml.
The container is now started successfully, but objects cannot be deserialized, because the custom deserializer is not used. I get a MarshallingException caused by
Caused by: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of
org.geojson.LngLatAlt out of START_ARRAY token
at [Source: java.io.StringReader#33714932; line: 1, column: 385] (through reference chain:
org.drools.core.command.runtime.BatchExecutionCommandImpl["commands"]->
org.drools.core.command.runtime.rule.InsertObjectCommand["object"]->
MYPACKAGE.MYCLASS["polygon"]->org.geojson.Polygon["coordinates"])
at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163)
This is exactly the part the custom deserializer should take care of.
Update: Karel Suta's answer helped me to investigate further.
The issue 5776 to the PR indicates that annotating a DTO should be possible to extend the JSON marshaller. The new JSONMarshallerExtension should only be needed if the object model is not to be annotated.
https://issues.jboss.org/browse/JBPM-5776
https://github.com/kiegroup/droolsjbpm-integration/pull/851
Enabling debug log level for org.kie.server, I got:
DEBUG [org.kie.server.services.drools.DroolsKieServerExtension] (default task-1) Adding org.geojson.Polygon type into extra jaxb classes set
DEBUG [org.kie.server.services.drools.DroolsKieServerExtension] (default task-1) Added org.geojson.Polygon type into extra jaxb classes set
DEBUG [org.kie.server.services.drools.DroolsKieServerExtension] (default task-1) Adding org.geojson.LngLatAlt type into extra jaxb classes set
DEBUG [org.kie.server.services.drools.DroolsKieServerExtension] (default task-1) Added org.geojson.LngLatAlt type into extra jaxb classes set
...
and
DEBUG [org.kie.server.api.marshalling.BaseMarshallerBuilder] (default task-2) About to build default instance of JSON marshaller with classes [
..., class org.geojson.Polygon, org.geojson.LngLatAlt, ...
Together with over hundred other classes (all dependencies it seems). So the classes are recognized and maybe supplying a JSONMarshallerExtension for them would work (I have not figured out, how to do that), but it should be possible with annotations as well.
Any other suggestions to get the annotations working?

In latest snapshot it could be possible to define custom marshaller for classes using JSONMarshallerExtension, see this PR.

Related

#field:JsonProperty vs #JsonProperty at runtime vs swagger doc

I'm writing a new app with Quarkus framework and Kotlin languague. If that matters, Gradle is configured with the quarkus reactive libraries but i'm not using them for now.
So i had this bean as input of a REST resource :
#JsonInclude(JsonInclude.Include.NON_EMPTY)
data class DemoRestRequest(
#JsonProperty("public_name")
val internalName: String?,
#JsonProperty("public_day")
val internalDay: LocalDate
}
used by an endpoint declared as such :
#Path("/bean")
#POST
fun testBeanValidationException(
bean: DemoRestRequest
): RestResponse<DemoRestResponse> { ... }
The HTTP endpoint is functional when called. Especially, the mandatory member "public_day" is indeed mandatory.
Issue A/ (apparently solved)
I noticed that the Swagger documention referenced the internal names of the members, which is bad, until I randomly experimented with
#JsonInclude(JsonInclude.Include.NON_EMPTY)
data class DemoRestRequest(
#field:JsonProperty("public_name")
val internalName: String?,
#field:JsonProperty("public_day")
val internalDay: LocalDate
}
With this change, the Swagger doc was fine.
Issue B/
Then at runtime when calling the endpoint, i received a 400 Bad Request by the framework because the mandatory field of the bean was considered missing, even though I provided it under the name "public_name". The call was OK if I provided the field with its internal name.
Considering the starting point, i tried this :
#JsonInclude(JsonInclude.Include.NON_EMPTY)
data class DemoRestRequest(
#field:JsonProperty("public_name")
val internalName: String?,
#field:JsonProperty("public_day")
#JsonProperty("public_day")
val internalDay: LocalDate
}
and now, both Swagger and the framework at runtime are OK.
Can somebody explains me why i need two annotations for those 2 differents needs and why the behaviour of a nullable vs non nullable fields is different ?
Issue C (bonus)/
That one is a bonus as i guess it's related the web frameworks themselves rather than languages.
If I put JPA annotation to the bean fields, or simply if I don't provide the mandatory field, the Java exception that bubble up to the end user with a "400 Bad Request" will contain the internal name of the invalid/missing field(s).
I can imagine this is understandable given we are now under the hood and not anymore at mapping time between public and internal names.
Nevertheless, i'd like to provide my application's clients a nice XP and tell them which field is generating a 400. Thus i need to get the public name... Not sure how to do that without reimplementing a web framework...

Why does Quarkus complain about my (unused) #ApplicationScoped data class with a #ConfigProperty property?

This kotlin data class is part of an internal library used in some Quarkus microservices (Quarkus 2.0.0.Final):
#ApplicationScoped
data class FooConfiguration(
#ConfigProperty(name = "foo.bar")
val fooBar: String
)
The library is used in a few microservices, and most of them do use that configuration. But some are not. For those, the foo.bar property is not defined in the application.properties. I would expect this to not matter at all, as those services are never injecting a FooConfiguration, so I'd expect that to never be constructed.
However, the application refuses startup with this error message:
SRCFG00014: The config property foo.bar is required but it could not be found in any config source
I know how to workaround this issue (simply supplying a nonsense value), but I currently wonder why this is an issue in the first place. The configuration bean should never get constructed. Why is this happening?
This is a MicroProfile Config-related issue.
Your foo.bar property is optional, not required, in the parlance of the MicroProfile Config specification, because a value for it is not present in any configuration source, as you have indicated. To inject a value for an optional MicroProfile Config configuration property, you need to use java.util.Optional. I don't know Kotlin, so here is what it would look like in Java:
#Inject
#ConfigProperty(name = "foo.bar")
private Optional<String> fooBar;

Class Redefine not working

SO I am trying to redefine a class. I have a class named folder. In OSGi (using Felix) I have a new Folder class with the same methods but some additional logging.
I am trying to take the Folder Class from Felix and redefine the main Folder class on the main classloader
I do have the agent set on startup.
new ByteBuddy()
.redefine(Class.forName(classToOverride.trim()), ClassFileLocator.ForClassLoader.of(felixClassLoader))
.name(classToOverride.trim())
.make() .load(contextClassLoader);
I have tried different strategies in the load method.
Without any strategies I get the following error
Caused by: java.lang.IllegalStateException: Cannot inject already loaded type: class com.dotmarketing.portlets.folders.model.Folder
at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection.inject(ClassInjector.java:187) ~[byte-buddy-1.6.12.jar:?]
at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$Default$InjectionDispatcher.load(ClassLoadingStrategy.java:187) ~[byte-buddy-1.6.12.jar:?]
at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$Default.load(ClassLoadingStrategy.java:120) ~[byte-buddy-1.6.12.jar:?]
at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:79) ~[byte-buddy-1.6.12.jar:?]
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:4376) ~[byte-buddy-1.6.12.jar:?]
at com.dotmarketing.osgi.GenericBundleActivator.publishBundleServices(GenericBundleActivator.java:177) ~[dotcms_4.1.0_563a5c3.jar:?]
With ClassReloadingStrategy.fromInstalledAgent I get no error but doesn't work.
On a JVM, you cannot simply redefine an already loaded class. You can only redefine a class using a Java agent where Byte Buddy supplies the AgentBuilder API which you can use. Note that it is only possible to change the content of methods but not a class's layout. You probably want to have a look at the Advice API to do so.

JAX-RS: serializing a POJO fails on payara micro

After moving our application war from Glassfish3 to a deployment with Payara Micro, the JAX-RS serialization (jersey + jackson) doesn't work any more.
Thanks to Adam, we solved the issue with serializing pure collections, we now encounter similar errors when returning POJOs:
#GET
#Produces("application/json")
public BirdyTO findAllDaBirdy() {
return getBirdy();
}
where BirdyTO is a POJO which contains other POJOS and/or collections of POJOS.
That one gives us the error:
MessageBodyWriter not found for media type=application/json;charset=utf-8, type=class org.example.BirdyTO, genericType=class org.example.BirdyTO.
Strange thing is that similar interfaces in same application work fine.
Any idea?
Mapping of POJOs to JSON is not standardized in Java EE. Glassfih 4/Payara use MOXy to map POJO to JSON by default, which uses JAXB for the mapping. See [this post by Reza Rahman] (https://blogs.oracle.com/theaquarium/entry/moxy_is_the_new_default). It is possible that BirdyTO cannot be mapped by Moxy.
If you want to use Jackson, you have to:
disable default Moxy feature (by setting jersey.config.server.disableMoxyJson property to true)
add Jackson library into your app (com.fasterxml.jackson.jaxrs)
turn on the JacksonFeature (provided by the Jackson library) in your JAX-RS application
More info how to do it in this answer: Customizing JSON marhsalling with GlassFish v4

Migrating stateful session bean from EJB 2.1 to EJB 3 - how to migrate create method having args

Im trying to migrate a stateful session bean from EJB 2.1 to EJB 3.0, the home interface of the bean which extends EJBHome has a create method with two args and the corresponding bean has a matching args ejbcreate method and one more no arg ejbcreate method.
My question is-
1. do I need to create two constructors one no arg and one arg to migrate this stateful session bean?
2. The ejbcreate method code is throwing "CreateException" and a run time exception, as of now ejbcreate defines throws "CreateException", do i need to define thorws CreateException" on the constructor or can I skip the create exception throwing part in the code of the constructor.
Other alternative I see posted in one blog is creating a method and annotating with #init, though not sure if this is the way as they were talking about EJB2 client view for a EJB3 bean.
There is unfortunately no way to specify arguments while creating a stateful session bean using EJB 3, so you'll need to add an initialize(arg1, arg2) method and call it after obtaining in instance via JNDI.
Only the no-arg constructor can be used in EJB 3.
Yes, #Init is the equivalent of ejbCreate when using annotations to define the EJB 2 client view when using EJB 3 style bean definition.