users@jersey.java.net

Re: [Jersey] Problem: Jersey swallows all JAXP exceptions silently !

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Fri, 24 Jul 2009 08:51:32 +0200

On Jul 24, 2009, at 2:42 AM, Morten wrote:

> Jersey calls into JAXP to marshall returned objects automatically.
> When it works, this is very nice as it allows clean code like the
> code below:
>
> HOWEVER, if SomeObject marshalling fails due to a some problem
> inside SomeObject (f.x. a wrong annotation) then Jersey silently
> ignores the exception and simply returns an empty result. Even if I
> install an ExceptionMapper for java.lang.Throwable.
>
> This is VERY, VERY annoying as it takes a LOT of time to diagnose
> and debug such cases. Jersey should not swallow (JAXP) exceptions.
> At least a valid registered ExceptionMapper should know of them so I
> can see it, log it and act on it!
>
> Jersey code example:
> @GET
> public SomeObject getServiceModule() throws Exception
> {
> // Note: Hopefully SomeObject can marshal correctly because
> // jersey will ignore any exceptions after the object was created.
> return SomeObject(42);
> }
>

Did a 500 error get returned to the client? Did an exception logged on
the server side? If so can you send the stack trace to me (i would
like to know if we can improve the logging in this area).

An instance of SomeObject will be serialized directly to the HTTP
container's output stream at the last possible moment. Once the first
byte has been written to that output stream the status code and
headers are set and the response is committed and another response
cannot be written. Thus, in those cases it is not possible to map an
exception to a response using an ExceptionMapper.

If there is an error while writing the response but the response has
not been committed then there is an opportunity to map exceptions.
Under these cases JAX-RS specifies that only WebApplicationException
instances shall be mapped, essentially implemented by the following
code:

         try {
             response.write();
         } catch (WebApplicationException e) {
             if (response.isCommitted()) {
                 LOGGER.log(Level.SEVERE, "The response of the
WebApplicationException cannot be utilized " +
                         "as the response is already committed. Re-
throwing to the HTTP container", e);
                 throw e;
             } else {
                 mapWebApplicationException(e, response);
                 response.write();
             }
         }

I am not quite sure why we limited mapping to WebApplicationException
in such circumstances.

Here is the code to for writing the JAXB object:

     public final void writeTo(
             Object t,
             Class<?> type,
             Type genericType,
             Annotation annotations[],
             MediaType mediaType,
             MultivaluedMap<String, Object> httpHeaders,
             OutputStream entityStream) throws IOException {
         try {
             final Marshaller m = getMarshaller(type, mediaType);
             final Charset c = getCharset(mediaType);
             if (c != UTF8) {
                 m.setProperty(Marshaller.JAXB_ENCODING, c.name());
             }
             writeTo(t, mediaType, c, m, entityStream);
         } catch (JAXBException cause) {
             throw ThrowHelper.withInitCause(cause,
                     new
IOException(ImplMessages.ERROR_MARSHALLING_JAXB(t.getClass()))
                     );
         }
     }

     protected void writeTo(Object t, MediaType mediaType, Charset c,
             Marshaller m, OutputStream entityStream)
             throws JAXBException, IOException {
         m.marshal(t, entityStream);
     }


Any JAXB exception is currently wrapped around an IOException. Perhaps
we should instead throw a new WebApplicationException(500) with the
JAXBException as the cause. That would at least give an opportunity to
map in the case when the response has not been committed.

Paul.