Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to provide custom configuration for JAXB context #25801

Merged
merged 1 commit into from Jun 15, 2022

Conversation

Sgitario
Copy link
Contributor

@Sgitario Sgitario commented May 26, 2022

Changes added as part of this pull request:

When using the quarkus-resteasy-reactive-jaxb extension there are some advanced features that RESTEasy Reactive supports.

  1. Inject JAXB components

The JAXB resteasy reactive extension will serialize and deserialize requests and responses transparently for users. However, if you need finer usage of the JAXB components, you can inject either the JAXBContext, Marshaller, or Unmarshaller components into your beans:

@ApplicationScoped
public class MyService {

    @Inject
    JAXBContext jaxbContext;

    @Inject
    Marshaller marshaller;

    @Inject
    Unmarshaller unmarshaller;

    // ...
}

Note: Quarkus will automatically register all the classes annotated with @XmlRootElement to the JAXB context.

  1. Customize the JAXB configuration

To customize the JAXB configuration for either the JAXB context, and/or the Marshaller/Unmarshaller components, the suggested approach is to define a CDI bean of type io.quarkus.jaxb.runtime.JaxbContextCustomizer.

An example where a custom module needs to be registered would look like so:

@Singleton
public class RegisterCustomModuleCustomizer implements JaxbContextCustomizer {

    // For JAXB context configuration
    @Override
    public void customizeContextProperties(Map<String, Object> properties) {

    }

    // For Marshaller configuration
    @Override
    public void customizeMarshaller(Marshaller marshaller) throws PropertyException {
        marshaller.setProperty("jaxb.formatted.output", Boolean.TRUE);
    }

    // For Unmarshaller configuration
    @Override
    public void customizeUnmarshaller(Unmarshaller unmarshaller) throws PropertyException {
        // ...
    }
}

Note: It's not necessary to implement the three methods, but only the want you need.

Alternatively, you can provide your own JAXBContext bean by doing:

public class CustomJaxbContext {

    // Replaces the CDI producer for JAXBContext built into Quarkus
    @Singleton
    JAXBContext jaxbContext() {
        // ...
    }
}

Note: By replacing the JAXB context, you need to bound the classes to be serialized/deserialized and also configure the JAXB context accordingly.

Resolves #25753

Copy link
Contributor

@geoand geoand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this!

I added an initial round of comments

@Sgitario
Copy link
Contributor Author

Thanks for this!

I added an initial round of comments

Many thanks!
I think I've already addressed all your comments. Plus, I've also deleted the stringClassesToBeBound from JaxbContextConfigRecorder.

@Sgitario Sgitario marked this pull request as ready for review May 26, 2022 07:39
@Sgitario Sgitario requested a review from geoand May 26, 2022 07:39
@geoand
Copy link
Contributor

geoand commented May 26, 2022

Thanks for this!
I added an initial round of comments

Many thanks! I think I've already addressed all your comments. Plus, I've also deleted the stringClassesToBeBound from JaxbContextConfigRecorder.

Great!

I'll take another look later.
@Postremus, @gsmet do you also want to have a look?

@Sgitario Sgitario force-pushed the feat_customize_jaxb branch 2 times, most recently from 54e1f05 to 35116c9 Compare June 7, 2022 06:27
@quarkus-bot

This comment has been minimized.

@gastaldi
Copy link
Contributor

gastaldi commented Jun 8, 2022

I like the idea, we should mark this as a noteworthy feature

@Sgitario Sgitario force-pushed the feat_customize_jaxb branch 2 times, most recently from 10b477f to 4393e39 Compare June 8, 2022 05:53
@Sgitario Sgitario requested a review from gastaldi June 8, 2022 05:56
Copy link
Contributor

@gastaldi gastaldi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@gastaldi
Copy link
Contributor

gastaldi commented Jun 8, 2022

Apparently the build is failing:

2022-06-08 14:50:46,826 ERROR [org.jbo.res.rea.com.cor.AbstractResteasyReactiveContext] (vert.x-eventloop-thread-0) Request failed: java.lang.RuntimeException: javax.xml.bind.JAXBException: io.quarkus.resteasy.reactive.jaxb.deployment.test.SimpleXmlTest$ModelWithoutAnnotation is not known to this context
	at io.quarkus.resteasy.reactive.jaxb.common.runtime.serialisers.JaxbMessageBodyReader.unmarshal(JaxbMessageBodyReader.java:67)
	at io.quarkus.resteasy.reactive.jaxb.common.runtime.serialisers.JaxbMessageBodyReader.doReadFrom(JaxbMessageBodyReader.java:76)
	at io.quarkus.resteasy.reactive.jaxb.common.runtime.serialisers.JaxbMessageBodyReader.readFrom(JaxbMessageBodyReader.java:36)
	at org.jboss.resteasy.reactive.server.handlers.RequestDeserializeHandler.readFrom(RequestDeserializeHandler.java:107)
	at org.jboss.resteasy.reactive.server.handlers.RequestDeserializeHandler.handle(RequestDeserializeHandler.java:67)

@quarkus-bot

This comment has been minimized.

@Sgitario
Copy link
Contributor Author

Sgitario commented Jun 9, 2022

Apparently the build is failing:

2022-06-08 14:50:46,826 ERROR [org.jbo.res.rea.com.cor.AbstractResteasyReactiveContext] (vert.x-eventloop-thread-0) Request failed: java.lang.RuntimeException: javax.xml.bind.JAXBException: io.quarkus.resteasy.reactive.jaxb.deployment.test.SimpleXmlTest$ModelWithoutAnnotation is not known to this context
	at io.quarkus.resteasy.reactive.jaxb.common.runtime.serialisers.JaxbMessageBodyReader.unmarshal(JaxbMessageBodyReader.java:67)
	at io.quarkus.resteasy.reactive.jaxb.common.runtime.serialisers.JaxbMessageBodyReader.doReadFrom(JaxbMessageBodyReader.java:76)
	at io.quarkus.resteasy.reactive.jaxb.common.runtime.serialisers.JaxbMessageBodyReader.readFrom(JaxbMessageBodyReader.java:36)
	at org.jboss.resteasy.reactive.server.handlers.RequestDeserializeHandler.readFrom(RequestDeserializeHandler.java:107)
	at org.jboss.resteasy.reactive.server.handlers.RequestDeserializeHandler.handle(RequestDeserializeHandler.java:67)

Yep... This is a sneaky test... It could not reproduce it locally, so I'm adding traces just to see what is going on.

@quarkus-bot

This comment has been minimized.

@quarkus-bot

This comment has been minimized.

@quarkus-bot

This comment has been minimized.

@quarkus-bot

This comment has been minimized.

@Sgitario
Copy link
Contributor Author

Apparently the build is failing:

2022-06-08 14:50:46,826 ERROR [org.jbo.res.rea.com.cor.AbstractResteasyReactiveContext] (vert.x-eventloop-thread-0) Request failed: java.lang.RuntimeException: javax.xml.bind.JAXBException: io.quarkus.resteasy.reactive.jaxb.deployment.test.SimpleXmlTest$ModelWithoutAnnotation is not known to this context
	at io.quarkus.resteasy.reactive.jaxb.common.runtime.serialisers.JaxbMessageBodyReader.unmarshal(JaxbMessageBodyReader.java:67)
	at io.quarkus.resteasy.reactive.jaxb.common.runtime.serialisers.JaxbMessageBodyReader.doReadFrom(JaxbMessageBodyReader.java:76)
	at io.quarkus.resteasy.reactive.jaxb.common.runtime.serialisers.JaxbMessageBodyReader.readFrom(JaxbMessageBodyReader.java:36)
	at org.jboss.resteasy.reactive.server.handlers.RequestDeserializeHandler.readFrom(RequestDeserializeHandler.java:107)
	at org.jboss.resteasy.reactive.server.handlers.RequestDeserializeHandler.handle(RequestDeserializeHandler.java:67)

Yep... This is a sneaky test... It could not reproduce it locally, so I'm adding traces just to see what is going on.

Maybe it was failing due to some other inconsistencies, but now it seems to be working all the time.

==== Advanced JAXB-specific features

When using the `quarkus-resteasy-reactive-jaxb` extension there are some advanced features that RESTEasy Reactive supports.

===== Inject JAXB components

The JAXB resteasy reactive extension will serialize and unserialize requests and responses transparently for users. However, if you need finer usage of the JAXB components, you can inject either the JAXBContext, Marshaller, or Unmarshaller components into your beans:

[source,java]
----
@ApplicationScoped
public class MyService {

    @Inject
    JAXBContext jaxbContext;

    @Inject
    Marshaller marshaller;

    @Inject
    Unmarshaller unmarshaller;

    // ...
}
----

[NOTE]
====
Quarkus will automatically register all the classes annotated with `@XmlRootElement` to the JAXB context.
====

===== Customize the JAXB configuration

To customize the JAXB configuration for either the JAXB context, and/or the Marshaller/Unmarshaller components, the suggested approach is to define a CDI bean of type `io.quarkus.jaxb.runtime.JaxbContextCustomizer`.

An example where a custom module needs to be registered would look like so:

[source,java]
----
@singleton
public class RegisterCustomModuleCustomizer implements JaxbContextCustomizer {

    // For JAXB context configuration
    @OverRide
    public void customizeContextProperties(Map<String, Object> properties) {

    }

    // For Marshaller configuration
    @OverRide
    public void customizeMarshaller(Marshaller marshaller) throws PropertyException {
        marshaller.setProperty("jaxb.formatted.output", Boolean.TRUE);
    }

    // For Unmarshaller configuration
    @OverRide
    public void customizeUnmarshaller(Unmarshaller unmarshaller) throws PropertyException {
        // ...
    }
}
----

[NOTE]
====
It's not necessary to implement the three methods, but only the want you need.
====

Alternatively, you can provide your own `JAXBContext` bean by doing:

[source,java]
----
public class CustomJaxbContext {

    // Replaces the CDI producer for JAXBContext built into Quarkus
    @singleton
    JAXBContext jaxbContext() {
        // ...
    }
}
----

[IMPORTANT]
====
By replacing the JAXB context, you need to bound the classes to be serialized/unserialized and also configure the JAXB context accordingly.
====
@geoand
Copy link
Contributor

geoand commented Jun 15, 2022

So is this ready or not?

@gastaldi gastaldi merged commit bd04794 into quarkusio:main Jun 15, 2022
@quarkus-bot quarkus-bot bot added this to the 2.11 - main milestone Jun 15, 2022
@quarkus-bot quarkus-bot bot added the kind/enhancement New feature or request label Jun 15, 2022
@Sgitario Sgitario deleted the feat_customize_jaxb branch June 15, 2022 17:00
@ylepikhov
Copy link

ylepikhov commented Jun 20, 2022

Hi! How about to provide a way to disable this feature via configuration? Or maybe separate it from core jaxb support in additional jar? Point is that someone (as I, for example) may have it's own code producing JAXBContext which may (or may not) conflict with this feature. Second point is injecting Marshaller/Unmarshaller in @ApplicationScoped beans may result in errors because Marshaller/Unmarshaller are not thread safe. I mean this feature is something beyond jaxb support for GraalVM and there is no just one right way to configure JAXBContext.

@geoand
Copy link
Contributor

geoand commented Jun 20, 2022

The JAXBContext, Marshaller and Unmarshaller beans are annotated with @DefaultBean, meaning that any user supplied beans will override what Quarkus provides.

Second point is injecting Marshaller/Unmarshaller in @ApplicationScoped beans may result in errors because Marshaller/Unmarshaller are not thread safe

@Sgitario I think we need to make these beans @RequestScoped... It may account for the flaky tests you have seen

@geoand
Copy link
Contributor

geoand commented Jun 20, 2022

#26231 should make the necessary changes

Sgitario added a commit to Sgitario/quarkus that referenced this pull request Jul 1, 2022
The REST Client Reactive JAXB was still using the `JAXB.marshall` / `JAXB.unmarshall` utilities that not allow users providing custom JAXB configuration. 

This change is related to quarkusio#25801 where it was done for the Resteasy Reactive JAXB extension.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Possibility to customize JAX-B Marshaller in quarkus-resteasy-reactive-jaxb
4 participants