You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@cxf.apache.org by Sergey Beryozkin <sb...@gmail.com> on 2013/12/02 13:41:57 UTC

Re: Handling exceptions in a JAX-RS fault interceptor when using Local Transport

Hi Mandy
On 30/11/13 18:49, Mandy Warren wrote:
> Hi,
>
> I just installed 2.7.8 today and realised that when I tested the fix below, I only tested it using an Interceptor which ran after JAXRSInInterceptor ie:
>
>         super(Phase.UNMARSHAL);
>         getAfter().add(JAXRSInInterceptor.class.getName());
>
> I've created a few more interceptors since including some that run at the Phase.RECEIVE. Looking at the documentation (http://cxf.apache.org/docs/jax-rs-basics.html#JAX-RSBasics-MappingexceptionsthrownfromCXFinterceptors ) you do clearly start that
> only interceptors which are registered after JAXRSInInterceptor (Phase.UNMARSHAL) and out interceptors registered before JAXRSOutInterceptor (Phase.MARSHAL) are supported. Are there any plans to support handling exceptions via an exception mapper from earlier / later in the
> chain?

Yes, it actually has been implemented correctly on the trunk, in fact I 
did it last Friday.

Unfortunately I was not able to merge to 2.7.9-SNAPSHOT, the fix I made 
on the trunk had been made possible after Dan's refactoring effort, with 
JAX-RS frontend having its own CXF Binding implementation on the trunk.

In CXF 2.7.x it depends on XML Binding which sets StaxOutInterceptor on 
the fault chain by default, this prevents the JAX-RS exception mapping 
done in the fault interceptor.

FYI, JAXRSOutExceptionMapperInterceptor has been deprecated too on the 
trunk, it is just much cleaner now on the trunk with respect to mapping 
native CXF exceptions with JAX-RS mappers. I stopped short of completely 
removing it knowing that several people may be using it now.

In meantime, in 2.7.x, if you need to map the exceptions thrown from 
interceptors running before JAXRSInInterceptor then you can do it with a 
custom out fault interceptor, the only restriction there is that if a 
text needs to be produced too then it can only be in XML format

Re interceptors running after JAXRSOutInterceptor, such faults will me 
mapped to now on the trunk (and with a custom fault out interceptor if 
needed) but I'm not sure it can be effective because JAXRSOutInterceptor 
will actually end up writing the data to the output stream.

Splitting JAXRSOutInterceptor is tricky because per the spec, if JAX-RS 
Message Body Writer throws an exception and if it can be mapped then the 
whole JAX-RS out chain needs to be rerun once (example, filters will run 
again, etc).
There might be a scope for some *optional* optimizations, example, if we 
know that XML is being produced then we can create a Stax writer and let 
the Stax ending interceptor finish it at the end of the chain, with 
custom interceptors updating the output in between, but it's going to be 
a sensitive refactoring, I'll try to review what can be realistically 
done in this regard in the next major CXF release

Thanks, Sergey



>
> Many thanks
>
> Mandy
>
> Sent from a mobile device
>
>> On 7 Oct 2013, at 11:23, Sergey Beryozkin <sb...@gmail.com> wrote:
>>
>> Hi Mandy
>>> On 06/10/13 22:11, Mandy Warren wrote:
>>> Hi Sergey,
>>>
>>> I finally got round to testing this tonight for handling both In & Out Interceptor faults & it works perfectly as described using 2.7.8-SNAPSHOT. This makes life so much easier!
>>
>> Sounds good, thanks for testing the latest code
>>
>>> Do you have any rough timescale as to when 2.7.8 will be released?
>>
>> I'm not sure, we may be able to do a 3.0 RC in a few weeks or so, 2.7.8 release might be considered too, but I'm not sure yet
>>
>> Thanks, Sergey
>>
>>>
>>> Many thanks
>>>
>>> Mandy
>>>
>>>> On 1 Oct 2013, at 13:23, Sergey Beryozkin wrote:
>>>>
>>>> I've actually merged it to CXF 2.7.x, but made the in fault mapping optional so as not to affect the existing in fault interceptors if any:
>>>>
>>>> https://cwiki.apache.org/confluence/display/CXF20DOC/JAX-RS+Basics#JAX-RSBasics-MappingexceptionsthrownfromCXFinterceptors
>>>>
>>>> Cheers, Sergey
>>>>> On 28/09/13 21:28, Mandy Warren wrote:
>>>>> This is great news Sergey, thanks so much for following this up! Please let me know when this is ready to use!
>>>>>
>>>>> Mandy
>>>>>
>>>>> Sent from a mobile device
>>>>>
>>>>>> On 25 Sep 2013, at 21:28, Sergey Beryozkin <sb...@gmail.com> wrote:
>>>>>>
>>>>>> Hi Mandy,
>>>>>>
>>>>>> FYI, after talking to you I thought I'd go ahead and finally resolve the long time pending issue of mapping the exceptions thrown from non-JAX-RS CXF interceptors to JAX-RS responses. I think it is mostly done now, after chatting to Dan and poking a bit it seems to be working OK, but on the trunk only, I may need to tweak it a bit more before documenting.
>>>>>> So if the exceptions can be mapped by ExceptionMappers then they won't make it to the fault chain where the mapping would have to be done manually - I'm keeping this change on the trunk as there could be the existing fault interceptors deployed but the good news is that it will become simpler to manage the exceptions thrown from the existing CXF interceptors on JAX-RS runtime paths...
>>>>>>
>>>>>> Thanks, Sergey
>>>>>>> On 19/09/13 21:31, Mandy Warren wrote:
>>>>>>> Many thanks this worked fine!
>>>>>>>
>>>>>>> Sent from a mobile device
>>>>>>>
>>>>>>>> On 13 Sep 2013, at 17:31, Sergey Beryozkin <sb...@gmail.com> wrote:
>>>>>>>>
>>>>>>>> Hi
>>>>>>>>> On 13/09/13 12:35, Mandy Warren wrote:
>>>>>>>>> Many thanks for your reply. I am now using an ExceptionMapper for application errors and have created a ResponseHandler to try an catch other interceptor exceptions but I can't work out how to get hold of the exception from the objects passed into the handleResponse method.
>>>>>>>>>
>>>>>>>>> I tried doing message.getExchange().getOutMessage().getContent(Exception.class)) but no luck..
>>>>>>>>>
>>>>>>>>> Please could you advise how this is possible?
>>>>>>>> The problem is that the exceptions thrown from the CXF interceptors escape the JAX-RS flow, ResponseHandler (or ContainerResponseFilter in CXF 3.0.0 SNAPSHOT) are part of the normal out JAX-RS chain and they do not see those exceptions.
>>>>>>>>
>>>>>>>> I'm presuming you throw the exceptions from CXF in interceptors, right ?
>>>>>>>> Try replacing them with RequestHandler (or ContainerRequestFilter if on CXF 2.7.x)
>>>>>>>>
>>>>>>>> I need to check if, optionally, we can handle the exceptions coming from other CXF in interceptors via JAX-RS and effectively ignore or fault out chain...
>>>>>>>>
>>>>>>>> Cheers, Sergey
>>>>>>>>
>>>>>>>>> Many thanks
>>>>>>>>>
>>>>>>>>> Sent from a mobile device
>>>>>>>>>
>>>>>>>>>> On 12 Sep 2013, at 21:05, Sergey Beryozkin <sb...@gmail.com> wrote:
>>>>>>>>>>
>>>>>>>>>> Hi
>>>>>>>>>>> On 12/09/13 18:32, Mandy Warren wrote:
>>>>>>>>>>> Hi,
>>>>>>>>>>>
>>>>>>>>>>> I am trying to write a Fault Interceptor to handle exceptions both from my
>>>>>>>>>>> application code & from other interceptors. The Interceptor needs to change
>>>>>>>>>>> the HTTP status code to something appropriate and set the message body with
>>>>>>>>>>> info that explains the error in more detail (either in JSON or XML
>>>>>>>>>>> depending on the request mime type).
>>>>>>>>>>>
>>>>>>>>>>> Many of the examples I have seen modify the HttpResponse using code such as:
>>>>>>>>>>>
>>>>>>>>>>>   HttpServletResponse response = (HttpServletResponse) message
>>>>>>>>>>>                  .getExchange().getInMessage()
>>>>>>>>>>>                  .get(AbstractHTTPDestination.HTTP_RESPONSE);
>>>>>>>>>>>
>>>>>>>>>>> however because we have integration tests which use the CXF local transport
>>>>>>>>>>> rather than HTTP this code doesn't seem to work.
>>>>>>>>>>>
>>>>>>>>>>> I need a solution which works for both HTTP & Local Transport.
>>>>>>>>>>>
>>>>>>>>>>> Here's the code I have so far but I get a null pointer thrown as the
>>>>>>>>>>> OutMessage seems to be null..
>>>>>>>>>>>
>>>>>>>>>>> public class MyFaultInterceptor extends AbstractPhaseInterceptor<Message> {
>>>>>>>>>>>>
>>>>>>>>>>>>      public CAPTWOFaultInterceptor() {
>>>>>>>>>>>>          super(Phase.PRE_STREAM);
>>>>>>>>>>>>      }
>>>>>>>>>>>>
>>>>>>>>>>>>      public void handleMessage(Message message) throws Fault {
>>>>>>>>>>>>
>>>>>>>>>>>>          Exception ex = message.getContent(Exception.class);
>>>>>>>>>>>>
>>>>>>>>>>>>          if (ex == null) {
>>>>>>>>>>>>              LOGGER.debug("unexpected null exception");
>>>>>>>>>>>>              throw new RuntimeException("Exception is expected");
>>>>>>>>>>>>          }
>>>>>>>>>>>>          if (!(ex instanceof Fault)) {
>>>>>>>>>>>>              LOGGER.debug("unexpected exception type");
>>>>>>>>>>>>              throw new RuntimeException("Fault is expected");
>>>>>>>>>>>>          }
>>>>>>>>>>>>
>>>>>>>>>>>>          Fault fault = (Fault)ex;
>>>>>>>>>>>>          Throwable causingException = fault.getCause();
>>>>>>>>>>>>
>>>>>>>>>>>>          LOGGER.debug("handling exception
>>>>>>>>>>>> {}"+causingException.getClass().getName());
>>>>>>>>>>>>
>>>>>>>>>>>>          if (causingException instanceof SomeBadException) {
>>>>>>>>>>>>              Response response =
>>>>>>>>>>>> Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
>>>>>>>>>>>              // outMessage seems to be null!
>>>>>>>>>>>
>>>>>>>>>>>              message.getExchange().getOutMessage().put(Response.class,
>>>>>>>>>>> response);
>>>>>>>>>>>          }
>>>>>>>>>>>
>>>>>>>>>>>          message.getInterceptorChain().abort(); // not sure if I need this!
>>>>>>>>>>>      }
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Any help would be much appreciated!
>>>>>>>>>> I think the only way to make it work in a portable way across multiple transports is to work with JAX-RS 2.0 ExceptionMapper (for catching the exceptions) and also replace the CXF interceptors with JAX-RS 2.0 in/out filters - this way it is guaranteed that JAX-RS Response will be produced and thus it will work even for Local transport.
>>>>>>>>>>
>>>>>>>>>> Using CXF interceptors in JAX-RS would also work with Local transport in normal flows but the exceptions thrown from such interceptors can only be handled in fault interceptors, where realistically you need to work with HttpServletResponse;
>>>>>>>>>>
>>>>>>>>>> I'm not sure why an out message is null; that solution probably won't work anyway, though may be we should look into it too; I'm not sure actually we have Local transport tests dealing with JAX-RS server errors, I'll have a look
>>>>>>>>>>
>>>>>>>>>> Sergey
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>> Many thanks
>>>>>>>>>>>
>>>>>>>>>>> Mandy
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> --
>>>>>>>>>> Sergey Beryozkin
>>>>>>>>>>
>>>>>>>>>> Talend Community Coders
>>>>>>>>>> http://coders.talend.com/
>>>>>>>>>>
>>>>>>>>>> Blog: http://sberyozkin.blogspot.com
>>>>>>>>
>>>>>>>>
>>>>>>>> --
>>>>>>>> Sergey Beryozkin
>>>>>>>>
>>>>>>>> Talend Community Coders
>>>>>>>> http://coders.talend.com/
>>>>>>>>
>>>>>>>> Blog: http://sberyozkin.blogspot.com
>>>>
>>>>
>>>> --
>>>> Sergey Beryozkin
>>>>
>>>> Talend Community Coders
>>>> http://coders.talend.com/
>>>>
>>>> Blog: http://sberyozkin.blogspot.com
>>
>