Hi

apologies for a delay.
I'm still not seeing the whole picture so I'd like to clarify few things.

You have a bridge service delegating to an external search service via an
injected jaxrs:client proxy.
The problem happens on the when a message with a 400 status code is being
returned, specifically, DataBindingProvider is erroneously asked to
deserialize it into an InputStream as opposed to a JAXB bean.

Where exactly does it happen :

-  In the bridge server response chain, after it has invoked on the external
jaxrs:client and the method where this invocation is done completes ?
- in the jaxrs:client response chain ?
- somewhere else ?

Is it true that a message with a 400 status also returns some error
description in the body ?

I suspect what is happening is this, assuming the error is in the
jaxrs:client response chain :  its search method returns
SearchResultsElement but the actual body has to be mapped to ErrorElement.
If the status > 400 but the response body is not empty then a message body
provider, if available, will be asked to read it anyway. This works ok for
WebClients expecting a Response but for proxies 'statically' expecting
SearchResultsElement (as in this case) it does not work well, unless the
body can actually be mapped into SearchResultsElement.
So DataBindingProvider which is shared in this case by jaxrs:server and
jaxrs:client is asked if it can handle data for SearchResultsElement, its
isReadable return true, so yes, and next it is asked to map ErrorElement
data into SearchResultsElement.

This is just one possible explanation. Please clarify what exactly is
happening and when.
How do you intend to process ErrorElements given that a proxy expects
SearchResultsElement ?

The other possible issue is that jaxrs:server and jaxrs:client share the
same DataBindingProvider. What you can try to do is to register *another
instance* of the same DataBindingProvider with jaxrs:client and see what
happens...

It is tricky for me to tell what exactly is happening. If you get stuck then
please attach a test case to a jira or perhaps do some debugging with the
CXF source ?

thanks, Sergey



On Wed, Mar 24, 2010 at 2:19 PM, tandem <[email protected]> wrote:

>
> The server method returns a JAXBObject, the expected status code is 400 and
> the ResponseExceptionHandler is not applicable as the method does not
> declare any exceptions.
>
> I think that either I have missed configure the intercepters or the custom
> fault handler is not correct, i.e. writing to the HTTPResponse outputStream
> instead of something cxf (see AbstractCustomFaultInterceptor below).
>
> Here is the code:
>
> bridge.war
>
> cxf/spring config:
>
>        <jaxrs:server id="wsBridgeServer" address="/">
>                <jaxrs:serviceBeans>
>                        <bean
> class="uk.co.company.bridge.service.rest.RestSearchService"
> p:searchServiceClient-ref="externalSearchService"/>
>                </jaxrs:serviceBeans>
>                <jaxrs:extensionMappings>
>                        <entry key="xml" value="application/xml" />
>                </jaxrs:extensionMappings>
>                <jaxrs:features>
>                        <cxf:logging/>
>                </jaxrs:features>
>                <jaxrs:dataBinding>
>                        <bean class="org.apache.cxf.jaxb.JAXBDataBinding">
>                                 <property name="namespaceMap">
>                                        <map>
>                                                <entry key="
> http://purl.org/dc/terms/"; value="dc" />
>                                                <entry key="
> http://schema.company.co.uk/production/1"; value="" />
>                                        </map>
>                                </property>
>                        </bean>
>                </jaxrs:dataBinding>
>                <jaxrs:outFaultInterceptors>
>                        <bean
> class="uk.co.company.protocol.rest.interceptor.CustomOutFaultInterceptor"
>
>  p:errorResponseProvider-ref="bridgeErrorResponseProvider" />
>                </jaxrs:outFaultInterceptors>
>                <jaxrs:providers>
>                        <bean
>
> class="uk.co.company.protocol.rest.provider.CustomWebApplicationExceptionMapper"
>
>  p:errorResponseProvider-ref="bridgeErrorResponseProvider" />
>                </jaxrs:providers>
>        <jaxrs:outInterceptors>
>                        <bean
>
> class="uk.co.company.protocol.service.rest.interceptor.NoCacheRequestInterceptor"
> />
>                </jaxrs:outInterceptors>
>        </jaxrs:server>
>
>
>        <bean id="externalSearchServiceClientFactory"
> class="uk.co.company.protocol.service.ClientFactory"
>
>  p:service="uk.co.company.search.service.ExternalSearchService"
>                        p:serviceUri="${protocol.service.location.search}"/>
>
>        <!-- getClient() return (T)JAXRSClientFactory.create(serviceUri,
> service)
> -->
>        <bean id="externalSearchService"
> factory-bean="externalSearchServiceClientFactory"
> factory-method="getClient"
> />
>
>
> search-client:
>
>        @Path("/search")
>        public interface ExternalSearchService {
>
>                @GET
>                @Path("{count}/{offset}")
>                @Produces({APPLICATION_XML})
>                SearchResultsElement search(@PathParam("count") int count,
> @PathParam("offset") int offset, @QueryParam("q") String searchTerm);
>
>        }
>
>
> search.war
>
> cxf/spring config:
>
>        <jaxrs:server id="wsSearchServer" address="/">
>                <jaxrs:serviceBeans>
>                        <bean
> class="uk.co.company.search.service.rest.RestSearchService" />
>                </jaxrs:serviceBeans>
>                <jaxrs:extensionMappings>
>                        <entry key="xml" value="application/xml" />
>                </jaxrs:extensionMappings>
>                <jaxrs:dataBinding >
>                        <bean class="org.apache.cxf.jaxb.JAXBDataBinding">
>                                <property name="namespaceMap">
>                                        <map>
>                                                <entry>
>                                                        <key>
>                                                                <value>
> http://purl.org/dc/terms/</value>
>                                                        </key>
>                                                        <value>dc</value>
>                                                </entry>
>                                                <entry>
>                                                        <key>
>                                                                <value>
> http://schema.company.co.uk/production/1</value>
>                                                        </key>
>                                                        <value></value>
>                                                </entry>
>                                        </map>
>                                </property>
>                        </bean>
>                </jaxrs:dataBinding>
>                <jaxrs:features>
>                        <cxf:logging/>
>                </jaxrs:features>
>                <jaxrs:outFaultInterceptors>
>                        <bean
> class="uk.co.company.protocol.rest.interceptor.CustomOutFaultInterceptor"
>
>  p:errorResponseProvider-ref="searchErrorResponseProvider" />
>                </jaxrs:outFaultInterceptors>
>                <jaxrs:providers>
>                        <bean
>
> class="uk.co.company.protocol.rest.provider.CustomWebApplicationExceptionMapper"
>
>  p:errorResponseProvider-ref="searchErrorResponseProvider" />
>                </jaxrs:providers>
>        </jaxrs:server>
>
>
> bridgeErrorResponseProvider & searchErrorResponseProvider beans are
> exception : status code mapping instances.
>
> CustomOutFaultInterceptor is an instance of:
>
>        public class AbstractCustomFaultInterceptor extends
> AbstractPhaseInterceptor<Message> {
>                ...
>
>                public AbstractCustomFaultInterceptor() {
>                        this(PRE_STREAM);
>                }
>
>                public AbstractCustomFaultInterceptor(String s) {
>                        super(MARSHAL);
>                }
>
>                public void handleMessage(Message message) throws Fault {
>                        handleMessageCalled = true;
>                        Fault fault = (Fault)
> message.getContent(Exception.class);
>
>                        final ErrorElement error =
> errorResponseProvider.create(fault.getCause());
>                        /*
>                         * ErrorElement JAXB object with String fields
> statusCode & message. This
> is want I expect/want to be in Response#getEntity()
>                         * If the services are called directly for negative
> path then the
> ErrorElement is marshalled to the expected xml. I presume that
>                         * this is due to the client being a WebClient and
> not a ClientProxyImpl.
>                         */
>
>                        final HttpServletResponse response =
> (HttpServletResponse)
> message.getExchange().getInMessage().get(
>                                        HTTP_RESPONSE);
>
>  response.setStatus(INTERNAL_SERVER_ERROR.getStatusCode());
>                        response.setContentType(APPLICATION_XML);
>
>                        try {
>                                final byte[] errorElement =
> getResponse(error);
>                                final OutputStream os =
> response.getOutputStream();
>                                os.write(errorElement);
>                                os.flush();
>                                message.getInterceptorChain().abort();
>                        } catch (IOException ioex) {
>                                throw new RuntimeException("Error writing
> the response");
>                         }
>                }
>        }
>
>
>
>
>
> Sergey Beryozkin-5 wrote:
> >
> > Hi
> >
> > I'm not quite sure what error you're seeing.
> > If an HTTP response status is >= 400 then, unless a given proxy method
> > explicitly returns Response, WebApplicationException will be thrown (or a
> > custom one provided ResponseExceptionHandler is registered). If an
> > exception
> > is thrown then one can still get a Response using
> > WebClient.client(proxy).getResponse.
> >
> > Response.getEntity() will return an error InputStream. What is not clear
> > is
> > how a custom provider (DataBindingProvider) ends up tryng to deserialize
> > such a Response, it should not happen.
> >
> > Can you post some code please ?
> > cheers, Sergey
> >
> > On Wed, Mar 24, 2010 at 5:36 AM, tandem <[email protected]> wrote:
> >
> >>
> >> I am talk between to jaxrs services in one tomcat and the happy path is
> >> working as expected. Upon error I am getting back an InputStream (i.e.
> >> sun.net.www.protocol.http.HttpURLConnection$HttpInputStream) from
> >> Response#getEntity() and originally before trying many different
> >> approaches
> >> I was getting a marshaling issue (Marshalling Error: class
> >> sun.net.www.protocol.http.HttpURLConnection$HttpInputStream nor any of
> >> its
> >> super class is known to this context) in the DataBindingProvider.
> >> Obviously
> >> the error makes sense but I am not sure what I have missed or if I
> simply
> >> should just catch the exception and the read the stream (which I am not
> >> entirely sure what to do with it once I have read it).
> >>
> >> Please let me know if some code will help explain the situation more.
> >> --
> >> View this message in context:
> >>
> http://old.nabble.com/JAXRSClientFactory-proxy-error-handling-tp28010855p28010855.html
> >> Sent from the cxf-user mailing list archive at Nabble.com.
> >>
> >>
> >
> >
>
> --
> View this message in context:
> http://old.nabble.com/JAXRSClientFactory-proxy-error-handling-tp28010855p28015729.html
> Sent from the cxf-user mailing list archive at Nabble.com.
>
>

Reply via email to