Giter Club home page Giter Club logo

Comments (8)

scottslewis avatar scottslewis commented on July 18, 2024

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

Yes that's right. To support the R7 spec I had to add to and modify somewhat the previous async support.

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?

It still works but under the OSGi async regime....see below.

* 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.

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",
"service.exported.intents=jaxrs", "osgi.basic.timeout=50000", "ecf.jaxrs.server.pathPrefix=/rs2" })

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:

https://github.com/ECF/JaxRSProviders/blob/master/examples/com.mycorp.examples.student.remoteservice.host/src/com/mycorp/examples/student/remoteservice/host/StudentServiceImpl.java

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.

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.

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) {
CompletableFuture cf = new CompletableFuture();
cf.complete(spacexAPI.callSpaceXMethod(request));
return cf;
}

This is basically what is done in the getStudentsCF() method in

https://github.com/ECF/JaxRSProviders/blob/master/examples/com.mycorp.examples.student.remoteservice.host/src/com/mycorp/examples/student/remoteservice/host/AbstractStudentService.java

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.

from jaxrsproviders.

scottslewis avatar scottslewis commented on July 18, 2024

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?

from jaxrsproviders.

modular-mind avatar modular-mind commented on July 18, 2024

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.

from jaxrsproviders.

scottslewis avatar scottslewis commented on July 18, 2024

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:

  1. ECF has some classes lto allow edef to be read/written without having to do all the spec-required formatting...e.g. ecf's EndpointDescription, EndpointDescriptionWriter and EndpointDescriptionReader.

e.g.

https://git.eclipse.org/c/ecf/org.eclipse.ecf.git/tree/osgi/bundles/org.eclipse.ecf.osgi.services.remoteserviceadmin/src/org/eclipse/ecf/osgi/services/remoteserviceadmin/EndpointDescriptionWriter.java

  1. The OSGi spec (and ECF's RSA impl) allows edef files to be imported at runtime...for 'discovering' remote services. These edef files can be embedded in bundles...and then the remote services will be imported when the containing bundle is activated. I have ECF examples of doing this if you need or want them.

  2. On both server and client there is a single instance of this RSA service: org.osgi.service.remoteserviceadmin.RemoteServiceAdmin. One of the methods on RemoteServiceAdmin is importService(EndpointDescription ed) which allows runtime control of the import process.

What I would suggest/propose is the following approach:

  1. Pick a part of the spacex api that you are interested in

  2. Declare a service interface (and arg/return types) that corresponds to one or two spacex methods (probably in new api bundle?)

  3. Work to put together the edef (as a file to start) for this remote service

  4. Get a simple consumer/test app working against the actual spacex service.

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.

  1. Modify the service interface (and edef) to issue async calls (with CompletableFuture?)

  2. Test as per 4.

wdyt?

from jaxrsproviders.

modular-mind avatar modular-mind commented on July 18, 2024

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).

from jaxrsproviders.

scottslewis avatar scottslewis commented on July 18, 2024

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.

from jaxrsproviders.

scottslewis avatar scottslewis commented on July 18, 2024

version 1.13.5 released with fix for this issue.

from jaxrsproviders.

modular-mind avatar modular-mind commented on July 18, 2024

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.

from jaxrsproviders.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.