Durante algo más de una semana he estado investigando un bug en el código de un API Jersey de una aplicación.

TL;DR

Si os encontráis la excepción java.lang.IllegalStateException: The resource configuration is not modifiable in this context. en las peticiones Jersey revisad si ha fallado algo durante la inicialización (no en la propia petición).

En algunos casos la aplicación arrancaba en un estado en el que todas las peticiones Jersey fallaban con el mismo error.

java.lang.IllegalStateException: The resource configuration is not modifiable in this context.
        at org.glassfish.jersey.server.ResourceConfig$ImmutableState.register(ResourceConfig.java:271)
        at org.glassfish.jersey.server.ResourceConfig$ImmutableState.register(ResourceConfig.java:219)
        at org.glassfish.jersey.server.ResourceConfig.register(ResourceConfig.java:449)
        at org.glassfish.jersey.servlet.WebComponent.<init>(WebComponent.java:331)
        at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:170)
        at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:358)
        at javax.servlet.GenericServlet.init(GenericServlet.java:158)
        at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1284)
        at org.apache.catalina.core.StandardWrapper.allocate(StandardWrapper.java:884)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:134)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Unknown Source)

El problema es que ese error no da mucha pista de donde puede estar el problema porque no hay ni una linea de código propio. Parecía como si la propia libreria no se inicializara correctamente. Además se arreglaba simplemente reiniciando la aplicación web dentro de Tomcat.

Tirando hacia atrás del git y revisando los cambios efectuados al final llegué al problema.

El API Jersey se inicializa en la aplicación mediante la anotación de esta clase.

@ApplicationPath("api")
public class MyApplication extends ResourceConfig {
   public MyApplication() {
       packages("net.sargue.tpv.api");
       register(RolesAllowedDynamicFeature.class);
   }
}

En el paquete net.sargue.tpv.api se inicializan algunos componentes en forma de clases, como el siguiente.

@Provider
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class GsonMessageBodyHandler implements MessageBodyWriter<Object>, MessageBodyReader<Object> {
    private static final Logger log = LoggerFactory.getLogger(GsonMessageBodyHandler.class);

    private Gson gson;
    private boolean debugJson = DB.getConfigDB().getBoolean("sync.debug.json", false);
    private long tsDebugJson = System.currentTimeMillis();
    ...

Resulta que el DB.getConfigDB()se inicializa al iniciar la aplicación (en el ServletContext) y ha de ser no nulo antes de servir cualquier petición. Pero la inicialización de Jersey ocurre en paralelo durante la inicialización de toda la aplicación así que, a veces, consultaba el valor antes de la inicialización y provocaba un NPE.

¿Y porqué tardé en verlo? Es una pregunta pertinente porque un NPE es una excepción clara, que ocurre en una linea muy concreta y todo muy facil de detectar… si miras en el fichero de log correcto. Resulta que para toda mi aplicación utilizo Logback como libreria de logs, y el log de la aplicación va a un fichero específico para esa aplicación. Tengo configurado tanto a nivel de contenedor de servlets como librerias como Jersey que me envie los logs a través de Logback. El problema es que el error ocurría durante la inicialización de la libreria, antes de configurar los logs a través de logback, e iba a parar al catalina.out. Además en local o en el servidor de test nunca había fallado y no veía el error. Y en producción recogia el log de la aplicación y no veía el error de inicialización. Hasta que busqué con más detenimiento.

En fin, que ya está arreglado y la moraleja es: no olvidar nunca el catalina.out !