Problem analysis & solution for updating RESTEasy MicroProfile REST Client, including a workaround for a bug in the Jakarta EE 8 API jar.

Photo by Edge2Edge Media, https://unsplash.com/

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.