cxf-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Adar Dembo <>
Subject Re: JAX-RS recursive dispatch?
Date Sat, 27 Apr 2013 02:27:27 GMT

Local transport certainly looks promising. I set it up with direct dispatch
and, as far as I can tell, it looks like the calling thread is also
responsible for method dispatch. That's awesome.

Now for the bad news. I got a toy example working with the WebClient API.
The client-side code looks something like this:

      WebClient client = WebClient.create("local://api");


However, I can't get it working with the proxy-based client. My code looks
like this:

    JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
    ApiRootResource root = bean.create(ApiRootResourceImpl.class);
    ClientConfiguration config = WebClient.getConfig(root);


 I keep getting an IllegalStateException with the message "Local
destination does not have a MessageObserver on address /clusters". Note
that this same code works correctly when the address is
so I don't suspect anything broken in either the client code or in my
resource layout.

I've spent a fair amount of time debugging this and I think I've figured
out what's going on. Every method call into the proxy (getRootV1(),
getClustersResource(), and readCluster()) creates a new proxy for the
corresponding subresource. That involves creating a new LocalClientState
(see ClientProxyImpl.invoke() for details). Here is its constructor:

    public LocalClientState(URI baseURI) {
        this.baseURI = baseURI;
        String scheme = baseURI.getScheme();
        if (!StringUtils.isEmpty(scheme) && scheme.startsWith(HTTP_SCHEME))
            this.currentBuilder = UriBuilder.fromUri(baseURI);
        } else {
            this.currentBuilder = UriBuilder.fromUri("/");

Because the baseURI in question begins with "local://", we end up in the
'else' statement, which resets our URI's path to '/'. In my case, the URIs
for each subcall end up looking like "/", "/v1", and "/clusters" instead of
"local://api", "local://api/v1", and "local://api/v1/clusters".

Later, during conduit selector preparation (called from inside
ClientProxyImpl.doChainedInvocation, via createMessage()), we try to find a
"compatible" conduit. We only have one conduit: a LocalConduit constructed
during the call to config.getConduit() in the client code. The conduit is
"compatible" if its endpoint's address matches the address in the message.
And the address in the message is based on those partial URIs I described
earlier. So what happens? The LocalConduit's endpoint's address is
"local://api", but the address in the message is "/clusters". They don't
match, and findCompatibleConduit() fails. We then construct a new
LocalConduit on-the-fly (see AbstractConduitSelector.getSelectedConduit()),
but the LocalDestination that's created for it (by LocalTransportFactory)
never gets an observer installed in it. The original LocalConduit (the one
that didn't match) has a destination with an observer; it was set up in

Now, I was able to make forward progress by, using my debugger, modifying
LocalClientState.currentBuilder in every proxy call to be an absolute path
like "local://api/...". At that point, the exception went away and the
message was routed to the server. But then
JAXRSInInterceptor.processMessage() failed to find the method to dispatch,
so I suspect I broke something in the process.

So, am I crazy? Or do proxy-based clients not work correctly with a local
transport? I'm using CXF 2.7.4.

On Fri, Apr 26, 2013 at 1:58 AM, Sergey Beryozkin <>wrote:

> On 26/04/13 09:48, Sergey Beryozkin wrote:
>> Hi,
>> On 26/04/13 00:11, Adar Dembo wrote:
>>> Thanks for the suggestion.
>>> Will using the WebClient in this way open a socket over the host's
>>> loopback interface? Since I'm keeping a database transaction open (via
>>> Hibernate) during this time, it's important that 1. the latency in the
>>> batch calls be minimal, and 2. the calls themselves be made by the same
>>> thread as the one that serviced the batch endpoint. Can I configure the
>>> WebClient in some way to get these guarantees?
>>>  I'm not sure we can have the current thread which invokes on the
>> endpoint run that endpoint's own external call completely.
>> Actually, if we get WebClient use CXF's Async HTTP Conduit (Apache HTTP
>> Client based), using WebClient#async switch to JAX-RS 2.0 AsyncInvoker
>> (starting from CXF 2.7.0), then I guess we won't have a single thread
>> involved, otherwise it should be a single thread. Dan, clarify please if
>> it is not quite the case.
>> I don't think we have any control over what happens at the socket level,
>> unless... Are you dealing with the the same host outbound calls ? If yes
>> - try using the local transport:
>> JAXRS+Testing#JAXRSTesting-**LocalTransport<>
>> src/test/java/org/apache/cxf/**systest/jaxrs/**
>> Finally, on the server, try using JAX-RS 2.0 AsyncDispatch (best from
>> CXF 2.7.4), that may help on its own, I'll work on documenting all 2.0
>> features asap.
> Sorry, meant AsyncResponse:
> javax/ws/rs/container/**AsyncResponse.html<>
>> Sergey

  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message