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