<chapter id="ExceptionHandling">
   <title>Exception Handling</title>

   <sect1 id="ExceptionMappers">
   <title>Exception Mappers</title>
   <para>

      ExceptionMappers are custom, application provided, components that can catch thrown application exceptions and
      write specific HTTP responses. They are classes annotated with @Provider and that implement this interface
   </para>
   <para>

      <programlisting>
         package jakarta.ws.rs.ext;

         import jakarta.ws.rs.core.Response;

         /**
         * Contract for a provider that maps Java exceptions to
         * {@link jakarta.ws.rs.core.Response}. An implementation of this interface must
         * be annotated with {@link Provider}.
         *
         * @see Provider
         * @see jakarta.ws.rs.core.Response
         */
         public interface ExceptionMapper&lt;E&gt;
         {
            /**
            * Map an exception to a {@link jakarta.ws.rs.core.Response}.
            *
            * @param exception the exception to map to a response
            * @return a response mapped from the supplied exception
            */
            Response toResponse(E exception);
         }
      </programlisting>
   </para>
   <para>

      When an application exception is thrown it will be caught by the &REST-API; runtime. &REST-API; will then scan registered
      ExceptionMappers to see which one support marshalling the exception type thrown. Here is an example of
      ExceptionMapper
   </para>
   <para>

      <programlisting>

         @Provider
         public class EJBExceptionMapper implements ExceptionMapper&lt;jakarta.ejb.EJBException&gt;
         {
            public Response toResponse(EJBException exception) {
               return Response.status(500).build();
            }
         }
      </programlisting>
   </para>
   <para>

      You register ExceptionMappers the same way you do MessageBodyReader/Writers. By scanning for @Provider
      annotated classes, or programmatically through the
      ResteasyProviderFactory class.
   </para>
      <para>
         As of RESTEasy 6.1 if a default <code>ExceptionMapper</code> is registered. This handles all uncaught
         exceptions and returns a response with the exceptions message and a status of 500. If the exception is a
         <code>WebApplicationException</code> the response from the exception is returned.
      </para>
      <para>
         The default <code>ExceptionMapper</code> will also log the exception at a debug level for debugging purposes.
         The logger name is <code>org.jboss.resteasy.core.providerfactory.DefaultExceptionMapper</code> which can be
         used to enabled/disable debug logging.
      </para>
   <para>


   </para>
   </sect1>
   <sect1 id="builtinException">
     <title>RESTEasy Built-in Internally-Thrown Exceptions</title>
     <para>RESTEasy has a set of built-in exceptions that are thrown by it when it encounters errors during dispatching or marshalling.  They all
           revolve around specific HTTP error codes.  You can find them in RESTEasy's javadoc under the package org.jboss.resteasy.spi.  Here's
           a list of them:</para>
<table frame="topbot">
<tgroup cols="3" rowsep="1" colsep="1">
<thead>
<row>
<entry>Exception</entry>
<entry>HTTP Code</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>ReaderException</entry>
<entry>400</entry>
<entry>All exceptions thrown from MessageBodyReaders are wrapped within this exception.  If there is no ExceptionMapper for the
wrapped exception or if the exception isn't a WebApplicationException, then resteasy will return a 400 code by default.</entry>
</row>
<row>
<entry>WriterException</entry>
<entry>500</entry>
<entry>All exceptions thrown from MessageBodyWriters are wrapped within this exception.  If there is no ExceptionMapper for the
wrapped exception or if the exception isn't a WebApplicationException, then resteasy will return a 400 code by default.</entry>
</row>
<row>
<entry>o.j.r.plugins.providers.jaxb.JAXBUnmarshalException</entry>
<entry>400</entry>
<entry>The &XML-BIND-API; providers throw this exception on reads.  They may be wrapping JAXBExceptions.  This class extends ReaderException</entry>
</row>
<row>
<entry>o.j.r.plugins.providers.jaxb.JAXBMarshalException</entry>
<entry>500</entry>
<entry>The &XML-BIND-API; providers throw this exception on writes.  They may be wrapping JAXBExceptions.  This class extends WriterException</entry>
</row>
<row>
<entry>ApplicationException</entry>
<entry>N/A</entry>
<entry>This exception wraps all exceptions thrown from application code.  It functions much in the same way as InvocationTargetException.
If there is an ExceptionMapper for wrapped exception, then that is used to handle the request.</entry>
</row>
<row>
<entry>Failure</entry>
<entry>N/A</entry>
<entry>Internal RESTEasy.  Not logged</entry>
</row>
<row>
<entry>LoggableFailure</entry>
<entry>N/A</entry>
<entry>Internal RESTEasy error.  Logged</entry>
</row>
<row>
<entry>DefaultOptionsMethodException</entry>
<entry>N/A</entry>
<entry>If the user invokes HTTP OPTIONS and no &REST-API; method for it, RESTEasy provides a default behavior by throwing this exception</entry>
</row>
<row>
<entry>UnrecognizedPropertyExceptionHandler</entry>
<entry>400</entry>
<entry>A Jackson provider throws this exception when JSON data is determine to be invalid.</entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1 id="ResteasyWebApplicationException">
   <title>Resteasy WebApplicationExceptions</title>
   <para>Suppose a client at local.com calls the following resource method:</para>
   <programlisting>
   @GET
   @Path("remote")
   public String remote() throws Exception {
      Client client = ClientBuilder.newClient();
      return client.target("http://third.party.com/exception").request().get(String.class);
   }
   </programlisting>
   <para>If the call to http://third.party.com returns a status code 3xx, 4xx, or 5xx, then the 
   <classname>Client</classname> is obliged by the &REST-API;
   specification to throw a <classname>WebApplicationException</classname>. Moreover, if the
   <classname>WebApplicationException</classname> contains a <classname>Response</classname>, which
   it normally would in RESTEasy, the server runtime is obliged by the &REST-API; specification to return that
   <classname>Response</classname>.
   As a result, information from the server at third.party.com, e.g., headers and body, will get sent back to
   local.com. The problem is that that information could be, at best, meaningless to the client
   and, at worst, a security breach.</para>

   <para>RESTEasy has a solution that works around the problem and still conforms to the &REST-API; specification.
   In particular, for each <classname>WebApplicationException</classname> it defines a new subclass:</para>

<programlisting>
WebApplicationException
+-ResteasyWebApplicationException
+-ClientErrorException
| +-ResteasyClientErrorException
| +-BadRequestException
| | +-ResteasyBadRequestException
| +-ForbiddenException
| | +-ResteasyForbiddenException
| +-NotAcceptableException
| | +-ResteasyNotAcceptableException
| +-NotAllowedException
| | +-ResteasyNotAllowedException
| +-NotAuthorizedException
| | +-ResteasyNotAuthorizedException
| +-NotFoundException
| | +-ResteasyNotFoundException
| +-NotSupportedException
| | +-ResteasyNotSupportedException
+-RedirectionException
| +-ResteasyRedirectionException
+-ServerErrorException
| +-ResteasyServerErrorException
| +-InternalServerErrorException
| | +-ResteasyInternalServerErrorException
| +-ServiceUnavailableException
| | +-ResteasyServiceUnavailableException
</programlisting>

    <para>The new <classname>Exception</classname>s play the same role as the original ones,
   but RESTEasy treats them slightly differently. When a <classname>Client</classname> detects
   that it is running in the context of a resource method, it will throw one of the new
   <classname>Exception</classname>s. However, instead of storing the original <classname>Response</classname>,
   it stores a "sanitized" version of the <classname>Response</classname>, in which only the status and
   the Allow and Content-Type headers are preserved. The original <classname>WebApplicationException</classname>,
   and therefore the original <classname>Response</classname>, can be accessed in one of two ways:</para>
   
   <programlisting>
// Create a NotAcceptableException.
NotAcceptableException nae = new NotAcceptableException(Response.status(406).entity("ooops").build());

// Wrap the NotAcceptableException in a ResteasyNotAcceptableException.
ResteasyNotAcceptableException rnae = (ResteasyNotAcceptableException) WebApplicationExceptionWrapper.wrap(nae);

// Extract the original NotAcceptableException using instance method.
NotAcceptableException nae2 = rnae.unwrap();
Assert.assertEquals(nae, nae2);

// Extract the original NotAcceptableException using class method.
NotAcceptableException nae3 = (NotAcceptableException) WebApplicationExceptionWrapper.unwrap(nae); // second way
Assert.assertEquals(nae, nae3);
   </programlisting>
   
   <para>Note that this change is intended to introduce a safe default behavior in the case that
   the <classname>Exception</classname> generated by the remote call is allowed to make its way up
   to the server runtime. It is considered a good practice, though, to catch the 
   <classname>Exception</classname> and treat it in some appropriate manner:</para>
   
<programlisting>
   @GET
   @Path("remote/{i}")
   public String remote(@PathParam("i") String i) throws Exception {
      Client client = ClientBuilder.newClient();
      try {
         return client.target("http://remote.com/exception/" + i).request().get(String.class);
      } catch (WebApplicationException wae) {
         ...
      }
   }
</programlisting>

   <para><emphasis role="bold">Note.</emphasis> While RESTEasy will default to the new, safer behavior, the original behavior can
   be restored by setting the configuration parameter "resteasy.original.webapplicationexception.behavior"
   to "true".</para>
</sect1>

<sect1 id="overring_resteasy_exceptions">
   <title>Overriding RESTEasy Builtin Exceptions</title>
   <para>You may override RESTEasy built-in exceptions by writing an ExceptionMapper for the exception.  For that matter, you can write an ExceptionMapper
   for any thrown exception including WebApplicationException</para>
</sect1>
</chapter>
