Thanks guys. Should I file a JIRA, or is there already one open on this?
On Sun, Apr 28, 2013 at 11:25 AM, Sergey Beryozkin <[email protected]>wrote: > Hi Andrei > > On 28/04/13 13:45, Andrei Shakirin wrote: > >> Looks like as a bug for me. >> What do you think, Sergey? >> >> You are right, it is a bug indeed with the way subresource proxies build > request URIs, we probably need to update that constructor a bit > > Thanks, Sergey > >> Cheers, >> Andrei. >> >> -----Original Message----- >>> From: Adar Dembo [mailto:[email protected]] >>> Sent: Samstag, 27. April 2013 04:27 >>> To: Sergey Beryozkin >>> Cc: [email protected] >>> Subject: Re: JAX-RS recursive dispatch? >>> >>> 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. >>> >>> >>> On Fri, Apr 26, 2013 at 1:58 AM, Sergey Beryozkin >>> <[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/**<https://cwiki.apache.org/**confluence/display/CXF20DOC/**> >>>>> JAXRS+Testing#JAXRSTesting- >>>>> >>>> **LocalTransport<https://**cwiki.apache.org/<https://cwiki.apache.org/> >>> >>>> JAXRS+confluence/display/**CXF20DOC/JAXRS+Testing#**JAXRSTesting- >>>>> >>>> LocalTra >>> >>>> JAXRS+nsport> >>>>> >>>>> http://svn.apache.org/repos/****asf/cxf/trunk/systests/jaxrs/****<http://svn.apache.org/repos/**asf/cxf/trunk/systests/jaxrs/**> >>>>> src/test/java/org/apache/cxf/****systest/jaxrs/** >>>>> JAXRSLocalTransportTest.java<h**ttp://svn.apache.org/repos/** >>>>> asf/cxf/trun <http://svn.apache.org/repos/asf/cxf/trun> >>>>> k/systests/jaxrs/src/test/**java/org/apache/cxf/systest/** >>>>> jaxrs/JAXRSLoca >>>>> lTransportTest.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/**<https://jax-rs-spec.java.net/**nonav/2.0-SNAPSHOT/apidocs/**> >>>> javax/ws/rs/container/****AsyncResponse.html<https://** >>>> jax-rs-spec.java.ne <https://jax-rs-spec.java.ne> >>>> t/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 >
