Hi

Thanks for the analysis, comments below

On 27/04/13 03:27, Adar Dembo wrote:
Sergey,

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");

WebClient.getConfig(client).getRequestContext().put(LocalConduit.DIRECT_DISPATCH,
Boolean.TRUE);
       client.path("v1/clusters/not-a-valid-cluster");
       client.get();

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

     JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
     bean.setAddress("local://api");
     bean.setResourceClass(ApiRootResourceImpl.class);
     ApiRootResource root = bean.create(ApiRootResourceImpl.class);
     ClientConfiguration config = WebClient.getConfig(root);
     config.getRequestContext().put(LocalConduit.DIRECT_DISPATCH,
Boolean.TRUE);
     config.getConduit();

root.getRootV1().getClustersResource().readCluster("not-a-valid-cluster");

  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
"http://localhost:<port>/api", 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 ServerImpl.start().

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.

I can see the tests where proxies are using the local transport, but you've indeed found an issue with proxy sub-resources creating wrong request URIs with non-HTTP transports (Local is one, I guess that might also apply to JMS), thanks for that, we'll need to fix that,

Cheers, Sergey



On Fri, Apr 26, 2013 at 1:58 AM, Sergey Beryozkin <[email protected]
<mailto:[email protected]>> 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:

        
https://cwiki.apache.org/__confluence/display/CXF20DOC/__JAXRS+Testing#JAXRSTesting-__LocalTransport
        
<https://cwiki.apache.org/confluence/display/CXF20DOC/JAXRS+Testing#JAXRSTesting-LocalTransport>

        
http://svn.apache.org/repos/__asf/cxf/trunk/systests/jaxrs/__src/test/java/org/apache/cxf/__systest/jaxrs/__JAXRSLocalTransportTest.java
        
<http://svn.apache.org/repos/asf/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSLocalTransportTest.java>


        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:
    
https://jax-rs-spec.java.net/__nonav/2.0-SNAPSHOT/apidocs/__javax/ws/rs/container/__AsyncResponse.html
    
<https://jax-rs-spec.java.net/nonav/2.0-SNAPSHOT/apidocs/javax/ws/rs/container/AsyncResponse.html>


        Sergey




--
Sergey Beryozkin

Talend Community Coders
http://coders.talend.com/

Blog: http://sberyozkin.blogspot.com

Reply via email to