[ 
https://issues.apache.org/jira/browse/CXF-4992?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Adar Dembo updated CXF-4992:
----------------------------

    Description: 
I was experimenting with direct dispatch local transport and ran into an issue 
with proxy-based clients.

Here's my working WebClient-based client code:

{code}
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();
{code}

Here's my broken proxy-based client code:

{code}
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");
{code}

What breaks? I get an IllegalStateException with the message "Local destination 
does not have a MessageObserver on address /clusters". Note that the 
proxy-based client code works correctly when the address is 
"http://localhost:<port>/api", so I don't suspect anything broken in either the 
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:

{code}
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("/");
    }
}
{code}

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.

I asked about this on the cxf-users mailing list 
(http://cxf.547215.n5.nabble.com/JAX-RS-recursive-dispatch-td5726752.html). The 
consensus was that this is a real bug, and likely affects other non-HTTP 
transports like JMS.

  was:
I was experimenting with direct dispatch local transport and ran into an issue 
with proxy-based clients.

Here's my working WebClient-based client code:

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

Here's my broken proxy-based client code:

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

What breaks? I get an IllegalStateException with the message "Local destination 
does not have a MessageObserver on address /clusters". Note that the 
proxy-based client code works correctly when the address is 
"http://localhost:<port>/api", so I don't suspect anything broken in either the 
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.

I asked about this on the cxf-users mailing list 
(http://cxf.547215.n5.nabble.com/JAX-RS-recursive-dispatch-td5726752.html). The 
consensus was that this is a real bug, and likely affects other non-HTTP 
transports like JMS.

    
> proxy sub-resources creating wrong request URIs with non-HTTP transports
> ------------------------------------------------------------------------
>
>                 Key: CXF-4992
>                 URL: https://issues.apache.org/jira/browse/CXF-4992
>             Project: CXF
>          Issue Type: Bug
>          Components: Transports
>    Affects Versions: 2.7.4
>            Reporter: Adar Dembo
>
> I was experimenting with direct dispatch local transport and ran into an 
> issue with proxy-based clients.
> Here's my working WebClient-based client code:
> {code}
> 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();
> {code}
> Here's my broken proxy-based client code:
> {code}
> 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");
> {code}
> What breaks? I get an IllegalStateException with the message "Local 
> destination does not have a MessageObserver on address /clusters". Note that 
> the proxy-based client code works correctly when the address is 
> "http://localhost:<port>/api", so I don't suspect anything broken in either 
> the 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:
> {code}
> 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("/");
>     }
> }
> {code}
> 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.
> I asked about this on the cxf-users mailing list 
> (http://cxf.547215.n5.nabble.com/JAX-RS-recursive-dispatch-td5726752.html). 
> The consensus was that this is a real bug, and likely affects other non-HTTP 
> transports like JMS.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira

Reply via email to