-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cannot get asynchronous JAX-RS calls working as expected #26
Comments
Yes that's right. To support the R7 spec I had to add to and modify somewhat the previous async support.
It still works but under the OSGi async regime....see below.
Yes. As per the spec, the remote service server has to have the one of the intents properties include the osgi.async intent...e.g. @component(immediate = true, property = { "service.exported.interfaces=*", "service.exported.intents=osgi.async", Note that osgi.basic.timeout=50000 allows the timeout value to be set (to 50 seconds in this case) for async access to that service. Here's an example from the source: What this does is that on the client/proxy the service interface return types of CompletableFuture, CompletionStage, IFuture, Future, and Promise are recognized as special and can timeout if not completed within the given osgi.basic.timeout value.
Maybe I'm not understanding you but I think you could do this quite easily. i.e. define a service interface that has method sig (e.g.): CompletableFuture method(SomeRequestType/Types); Then the implementation of this service interface have a reference to your spacex api and have it be implemented by: CompletableFuture method(SomeRequestType/Types request) { This is basically what is done in the getStudentsCF() method in Then the osgi.async intent, along with the osgi.basic.timeout will determine the timeout on the client if your call doesn't complete in time. Also...if you prefer it is/should still be possible to get the old EC async proxy behavior (i.e. with methodNameAsync) using the instructions for CompletableFuture toward the bottom of the page here: https://wiki.eclipse.org/Tutorial:_Building_your_first_Asynchronous_OSGi_Remote_Service It is possible that something about this legacy behavior is broken in the JaxRSProvider specifically...as I was mainly focused on supporting the R7 osgi.async behavior. |
After thinking about your question more I realize you may have been saying that you want an OSGi client (e.g. RCP) to access the spacex jaxrs service directly...i.e. with no server impl of the service at all...is that correct? |
Yes, I'm looking at using existing services like SpaceX that are provided by 3rd parties. The original ECF async clients worked great in that regard, and I'm wondering if there's a way to support that using either the new OSGi async spec or the older ECF way. My main goal is to support microservice development in the UI and for clients to not have to know anything about the service other than the REST API. Having asynchronous support in that scenario would be great. |
Hi Patrick. Ok, I get it. To be honest with you, I tested this use case (client-only access to a third-party hosted service) some time ago...and it was fine...but I haven't done so since the R7 version of things. So this is a good opportunity to do so :)...i.e. I would like to put together a good example of doing this here in this repo with a public service (seems like it might be from https://github.com/r-spacex/SpaceX-API). Let me know if this is ok with you...i.e. to make some of this into public example My belief is that either the R7 async support or the ECF 'legacy' async support should work for this use case...since both are essentially implemented on the server. My preference would be to focus on the R7 async approach if possible...since that's per-spec. But let me know if you can't or won't do that and I'll switch gears. The trick is going to be in 'fooling' the client into creating the appropriate proxy. My belief is that this can be done with the edef only. For example, here's the edef generated by server in the jersey example: <endpoint-descriptions xmlns="http://www.osgi.org/xmlns/rsa/v1.0.0">
<endpoint-description>
<property name="ecf.endpoint.id" value-type="String" value="http://localhost:8080/rservices/rs2"/>
<property name="ecf.endpoint.id.ns" value-type="String" value="ecf.namespace.jaxrs"/>
<property name="ecf.endpoint.ts" value-type="Long" value="1591467195512"/>
<property name="ecf.jaxrs.server.pathPrefix" value-type="String" value="/rs2"/>
<property name="ecf.rsvc.id" value-type="Long" value="1"/>
<property name="endpoint.framework.uuid" value-type="String" value="56f62b30-955a-42f9-887f-e27f9eead6db"/>
<property name="endpoint.id" value-type="String" value="670edfc6-1372-4b57-88c1-a708e379e4f2"/>
<property name="endpoint.package.version.com.mycorp.examples.student" value-type="String" value="1.0.0"/>
<property name="endpoint.service.id" value-type="Long" value="39"/>
<property name="objectClass" value-type="String">
<array>
<value>com.mycorp.examples.student.StudentService</value>
</array>
</property>
<property name="osgi.basic.timeout" value-type="String" value="50000"/>
<property name="remote.configs.supported" value-type="String">
<array>
<value>ecf.jaxrs.jersey.server</value>
</array>
</property>
<property name="remote.intents.supported" value-type="String">
<array>
<value>passByValue</value>
<value>exactlyOnce</value>
<value>ordered</value>
<value>osgi.async</value>
<value>osgi.private</value>
<value>osgi.confidential</value>
<value>jaxrs</value>
</array>
</property>
<property name="service.imported" value-type="String" value="true"/>
<property name="service.imported.configs" value-type="String">
<array>
<value>ecf.jaxrs.jersey.server</value>
</array>
</property>
<property name="service.intents" value-type="String">
<array>
<value>osgi.async</value>
<value>jaxrs</value>
</array>
</property>
</endpoint-description>
</endpoint-descriptions> A couple of things to note: <property name="service.intents" value-type="String">
<array>
<value>osgi.async</value>
<value>jaxrs</value>
</array>
</property> service.intents get's populated with the intents needed to support osgi.asyn and jaxrs (which is an intent for ECF's JaxRS RS provider). <property name="remote.intents.supported" value-type="String">
<array>
<value>passByValue</value>
<value>exactlyOnce</value>
<value>ordered</value>
<value>osgi.async</value>
<value>osgi.private</value>
<value>osgi.confidential</value>
<value>jaxrs</value>
</array>
</property> remote.supported.intents has all of the intents exposed by the distribution provider In OSGi R7 impl (and in this provider) at import time these values are used to determine what kind of proxy is created...i.e. whether it supports osgi.async or not. Note that the intent given in the component property (service.exported.intents) is not the same as service.intents in xml...that's due to the spec as there are a couple of different properties for specifying intents. There are a couple of things worth know for dealing with edef and the OSGI discovery mechanisms:
e.g.
What I would suggest/propose is the following approach:
I can/will help with the above...actually I will create a new example (api and consumer bundles) and checkin here if you like. I can/will give you commit access to this repo if you like.
wdyt? |
You're referencing the correct SpaceX documentation, and yes I would also be happy to contribute this work to an examples repo. You could create a JaxRSProvidersExamples repo if that would make things easier to organize. Otherwise I can contribute to the main JaxRSProviders repo as well. I currently have a very simple RCP client in this repo: https://github.com/modular-mind/spacex-client.git And there is a branch called "explore-async-services" which contains the async code I've been testing. There are only three projects which are very small. All you need to do is set the target and run the product config. It's currently set up to do a synchronous call to the SpaceX launch service, and that's working. To run the async version you just need to change the commented line in the LaunchPart object. FWIW, I think I have the EDEF as you specified in your message and I'm still getting the mapping error (cannot map to CompletableFuture). |
Enhanced WebResourceFactory class to properly handle async return types with complex objects (e.g. lists) as return types.
Enhanced WebResourceFactory class to properly handle async return types with complex objects (e.g. lists) as return types.
Good news. I was able to isolate the problem in class WebResourceFactory in the client. It wasn't properly handling return types, leading to the deserialization error you reported. After this fix, I'm able to run your code just fine in either sync or async. If you agree, I will produce another release including this fix tomorrow/Sunday/6/7/2020. Closing this issue. Once you get your spacex service to a reasonable point, I would be very happy to have part of it in this repo as an example of client-only remote services. I may try to break things out so that the same example can/could be run in karaf or eclipse rpc. |
version 1.13.5 released with fix for this issue. |
Thanks so much, this works great! I'll do some thinking this week about the example code and perhaps create an issue to have a place for discussion. |
I had the original ECF asynchronous calls working with version 1.1 of the client. I had an *Async interface that matched my original interface with *Async methods. Starting in version 1.3 this is no longer working, and I’m guessing this is because of work done to support the OSGi Asynchronous Service Specification:
https://docs.osgi.org/specification/osgi.cmpn/7.0.0/service.async.html
Previously, the ECF JAX-RS client (JaxRSClientContainer) would create a CompletableFuture and then use the non-async proxy to make the HTTP request. Now the client uses the async proxy itself, and this isn’t working right for me.
First, the JAX-RS annotations aren’t discovered on the async interface as they are only on the non-async interface. But when I add them to the async interface, the proxy is still trying to convert the HTTP response into a CompletableFuture (or other async return type) when it is actually receiving a regular object (Student, etc) in JSON from the server. The actual exception is something like:
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.concurrent.CompletableFuture<java.util.List<com.modumind.spacex.service.model.Launch>
I'm a little over my head here, but my questions are:
Should the legacy async mechanism still work? Is there something I'm not doing right to get this working?
If I should be using the new OSGi asynchronous call (specifying "osgi.async" in the EDEF), does this require that the service itself support that? I see in the JaxRSClientJacksonJaxbJsonProvider that its looking for the response header "X-OSGI-ASYNC-RETURN-TYPE", which makes me think the server is involved.
My use case is that I want to have the asynchronous support managed on the client so that I can call any REST service (I'm currently working with the SpaceX REST API). If this requires changes to the client I would be happy to do the work and submit a pr, but I need a little education first to see what I should be doing.
The text was updated successfully, but these errors were encountered: