Good morning,
It appears that using JAXRSClientFactoryBean to create proxy-beans for JAX-RS
webservices from within a TomEE 8.x container triggers a rather severe memory
leak via Johnzon's JSON-B provider's CDI integration in TomEE 8.x (I did not
test 9.x).
Specifically, if JAXRSClientFactoryBean is used while TomEEJsonbProvider (or
JsonbJaxrsProvider) is on the OpenEJB JAX-RS JSON providers list (either the
hard-coded default, or via 'openejb.jaxrs.jsonProviders'), then each call to
JAXRSClientFactoryBean.create() will cause a new JohnzonJsonb object to be
created and tracked by JohnzonCdiExtension. For example, the following code is
sufficient to trigger the issue if placed within a servlet class' doGet():
JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
bean.setServiceClass(IJaxrsWebservice.class);
bean.setAddress(http://127.0.0.1:8080/reproducer);
final IJaxrsWebservice proxy = bean.create(IJaxrsWebservice.class);
I have a sample project that includes this code and a mock JAX-RS webservice,
reproducing the issue; a copy of this is available in this GitHub repo:
https://github.com/deberhar/TomeeJohnzonBugReproducer. If I repeatedly call
the servlet, I can exhaust a 1GB application heap in ~4-6k GETs to the servlet
-- the server reports OutOfMemoryErrors, and a heap dump shows 924MB retained
by the JohnzonCdiExtension. If it would be helpful, I can provide a copy of
this project (what's the best way to do so?).
[Text Description automatically generated]
I believe the underlying cause is described in this Johnzon bug report:
https://issues.apache.org/jira/browse/JOHNZON-161. However, it seems the
Johnzon developers believe their CDI integration is behaving as expected, and
that it is the responsibility of the integrating container to cache and re-use
Jsonb instances, writing, in part:
> Hi skay, first of all it is a bug in your application so best is to fix your
> coding style. Now you have a workaround: set johnzon.skip-cdi=true or
> johnzon.cdi.activated=false in the jsonbconfig properties or set property
> johnzon.factory to new SimpleJohnzonAdapterFactory().
> Side note: in terms of performance, recreating Jsonb instances looses all
> caching and redo all the reflection so should be slow so in any case I
> recommend you to change the pattern you have (it is true for jackson too btw)
It appears the Meecrowave developers made changes in their source code to
prevent this leak via Johnzon's JsonbJaxrsProvider's CDI integration when used
with CXF; see https://issues.apache.org/jira/browse/MEECROWAVE-104 or
https://github.com/apache/openwebbeans-meecrowave/commit/eafd5b6eda81471c4abd61297a2455b2bca0a013.
For our organization's use of TomEE, we've been able to hack around this issue
by subclassing TomEEJsonbProvider to set johnzon.cdi.activated=false, and then
overriding the default provider list via openejb.jaxrs.jsonProviders. However,
this is likely not a proper solution.
What's the best way forward to addressing this properly?
Thank you,
David Eberhart
NYS Unified Court System
Office of Court Administration
Division of Technology and Court Research