Updating RESTEasy MicroProfile REST Client & dealing with a Jakarta EE 8 bug
Problem analysis & solution for updating RESTEasy MicroProfile REST Client, including a workaround for a bug in the Jakarta EE 8 API jar.
Problem Description
My simple ping application (on GitHub) exposes a single GET resource named “ping” that always returns the string “pong”. For testing this behavior, I use the highly convenient MicroProfile REST Client API, which is implemented by RESTEasy:
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client-microprofile</artifactId>
<version>4.5.10.Final</version>
<scope>test</scope>
</dependency>
When I tried to update RESTEasy to version 4.5.11.Final or beyond, I ran into the following exception during test execution:
java.lang.ExceptionInInitializerError
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:375)
at org.jboss.resteasy.core.ContextParameterInjector$1.run(ContextParameterInjector.java:59)
at org.jboss.resteasy.core.ContextParameterInjector$1.run(ContextParameterInjector.java:52)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:318)
at org.jboss.resteasy.core.ContextParameterInjector.<clinit>(ContextParameterInjector.java:51)
at org.jboss.resteasy.core.InjectorFactoryImpl.createParameterExtractor(InjectorFactoryImpl.java:249)
at org.jboss.resteasy.core.PropertyInjectorImpl.getParameterExtractor(PropertyInjectorImpl.java:133)
at org.jboss.resteasy.core.PropertyInjectorImpl.populateMap(PropertyInjectorImpl.java:73)
at org.jboss.resteasy.core.PropertyInjectorImpl.<init>(PropertyInjectorImpl.java:61)
at org.jboss.resteasy.core.InjectorFactoryImpl.createPropertyInjector(InjectorFactoryImpl.java:73)
at org.jboss.resteasy.core.providerfactory.Utils.injectProperties(Utils.java:75)
at org.jboss.resteasy.core.providerfactory.CommonProviders.addMessageBodyReader(CommonProviders.java:193)
at org.jboss.resteasy.core.providerfactory.CommonProviders.processProviderContracts(CommonProviders.java:87)
at org.jboss.resteasy.core.providerfactory.ClientHelper.processProviderContracts(ClientHelper.java:104)
at org.jboss.resteasy.core.providerfactory.ResteasyProviderFactoryImpl.processProviderContracts(ResteasyProviderFactoryImpl.java:841)
at org.jboss.resteasy.core.providerfactory.ResteasyProviderFactoryImpl.registerProvider(ResteasyProviderFactoryImpl.java:829)
at org.jboss.resteasy.core.providerfactory.ResteasyProviderFactoryImpl.registerProvider(ResteasyProviderFactoryImpl.java:816)
at org.jboss.resteasy.plugins.providers.RegisterBuiltin.registerProviders(RegisterBuiltin.java:109)
at org.jboss.resteasy.plugins.providers.RegisterBuiltin.register(RegisterBuiltin.java:74)
at org.jboss.resteasy.plugins.providers.RegisterBuiltin.getClientInitializedResteasyProviderFactory(RegisterBuiltin.java:54)
at org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl.getProviderFactory(ResteasyClientBuilderImpl.java:372)
at org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl.getConfiguration(ResteasyClientBuilderImpl.java:457)
at org.jboss.resteasy.microprofile.client.RestClientBuilderImpl.<init>(RestClientBuilderImpl.java:89)
at org.jboss.resteasy.microprofile.client.BuilderResolver.newBuilder(BuilderResolver.java:9)
at org.eclipse.microprofile.rest.client.RestClientBuilder.newBuilder(RestClientBuilder.java:54)
...
Caused by: java.util.MissingResourceException: Can't find bundle for base name javax.servlet.LocalStrings, locale en_US
at java.base/java.util.ResourceBundle.throwMissingResourceException(ResourceBundle.java:2045)
at java.base/java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:1683)
at java.base/java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:1586)
at java.base/java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:1549)
at java.base/java.util.ResourceBundle.getBundle(ResourceBundle.java:858)
at javax.servlet.ServletOutputStream.<clinit>(ServletOutputStream.java:44)
... 86 more
Problem Analysis
The stack trace above references the following line from ServletOutputStream
:
private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
private static ResourceBundle lStrings = ResourceBundle.getBundle(LSTRING_FILE);
Unfortunately, the properties file javax.servlet.LocalStrings
is not included in the dependency that I use for including the Jakarta EE 8 API:
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>8.0.0</version>
<scope>provided</scope>
</dependency>
This defect has already been observed by several developers in the past, and also filed as issue to the Jakarta EE API in March 2020 (but closed without a fix due to a missing response of the issue’s reporter).
A code refactoring in RESTEasy 4.5.11.Final (the introduction of the class org.jboss.resteasy.core.ContextServletOutputStream
and its use by org.jboss.resteasy.core.ContextParameterInjector
) triggered a new execution path that leads to this line in ServletOutputStream
, which explains why the problem did not appear in the previous RESTEasy version.
Problem Solution
Even though the required properties file is missing in the overall jakartaee-api
jar, it is contained in the following, more fine-grained specification artifact:
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>4.0.4</version>
<scope>test</scope>
</dependency>
Version 4 of the Jakarta Servlet API is the matching version for Jakarta EE 8.
Including this dependency in the test classpath finally solved the problem.