Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "OSGi R7 Remote Services between Python and Java"

(Running Hello Example in Karaf)
Line 124: Line 124:
 
7. Click the 'Debug OSGi' link in the upper right of the bndrun editor to start the example.
 
7. Click the 'Debug OSGi' link in the upper right of the bndrun editor to start the example.
  
Once started, it's necessary to give the following command in the console (to start the Java gateway listener)
+
Once started, at the gogo console prompt give the following command to start the Java gateway listener:
  
8. '''osgi>scr:enable org.eclipse.ecf.examples.hello.provider.ExamplePy4jProvider'''
+
<pre>
 +
osgi>scr:enable org.eclipse.ecf.examples.hello.provider.ExamplePy4jProvider
 +
</pre>
  
 
and
 
and
  
9. '''scr:enable org.eclipse.ecf.examples.hello.javahost.HelloImpl'''
+
<pre>
 +
osgi>scr:enable org.eclipse.ecf.examples.hello.javahost.HelloImpl
 +
</pre>
  
 
to enable the HelloImpl component and export it via the ecf.py4j.host provider.  This should output to the console the RSA endpoint description xml for the exported endpoint.  See the [https://github.com/ECF/Py4j-RemoteServicesProvider Python.Java provider repo for more info]
 
to enable the HelloImpl component and export it via the ecf.py4j.host provider.  This should output to the console the RSA endpoint description xml for the exported endpoint.  See the [https://github.com/ECF/Py4j-RemoteServicesProvider Python.Java provider repo for more info]
Line 147: Line 151:
 
karaf@root()>feature:install -v ecf-rs-examples-python.java-hello
 
karaf@root()>feature:install -v ecf-rs-examples-python.java-hello
 
</pre>
 
</pre>
 +
 +
2.  Enable the Python.Java distribution provider component
 +
 +
<pre>
 +
osgi>scr:enable org.eclipse.ecf.examples.hello.provider.ExamplePy4jProvider
 +
</pre>
 +
 +
3. Enable and export the HelloImpl service
 +
 +
<pre>
 +
osgi>scr:enable org.eclipse.ecf.examples.hello.javahost.HelloImpl
 +
</pre>
 +
 +
This should result in output to the console the RSA endpoint description xml for the exported endpoint.  See the [https://github.com/ECF/Py4j-RemoteServicesProvider Python.Java provider repo for more info]

Revision as of 18:51, 15 June 2018

This tutorial shows the use ofRemote Services and Python.Java Distribution Provider to create services between Python and Java. By 'create services between Python and Java' what is meant is both service implementations written in Java...with Python consumers, and service implementations in Python with Java consumers. The code presented below are examples, and everything about the examples (e.g. the service API, impl, and consumer code) can be replaced with one's own micro services.

With OSGi R7 and ECF's Photon/3.14.0 implementation, it's also possible to use Async Remote Services between Python and Java. This is also shown below.

Hello Service Interface

Here is a Java Interface that defines the 'IHello' service


import java.util.concurrent.CompletableFuture;
import org.osgi.util.promise.Promise;
 
public interface IHello {
 
    String sayHello(String from, String message);
    CompletableFuture<String> sayHelloAsync(String from, String message);
    Promise<String> sayHelloPromise(String from, String message);
}

The source with documentation is available here.

This interface defines one synchronous method 'sayHello(String from, String message)' and two async methods: 'sayHelloAsync', and 'sayHelloPromise'. These are asynchronous methods (as defined by the Async Remote Services) because of the CompletableFuture and Promise return types. These return types are interpreted by the distribution provider as async methods, and as such will not block the calling thread even if the communication is blocked or fails.

Here is a simple Java implementation of this service:


import java.util.concurrent.CompletableFuture;
import org.eclipse.ecf.examples.hello.IHello;
import org.osgi.service.component.annotations.Component;
import org.osgi.util.promise.Deferred;
import org.osgi.util.promise.Promise;
 
@Component(immediate=true,enabled=false,property = { "service.exported.interfaces=*",
"service.exported.configs=ecf.py4j.host",
"osgi.basic.timeout:Long=50000",
"service.intents=osgi.async"}) // osgi.async intent to get the async rs behavior
public class HelloImpl implements IHello {
 
    @Override
    public String sayHello(String from, String message) {
        System.out.println("Java.sayHello called by "+from+" with message: '"+message+"'");
        return "Java says: Hi "+from + ", nice to see you";
    }
    @Override
    public CompletableFuture<String> sayHelloAsync(String from, String message) {
        System.out.println("Java.sayHelloAsync called by "+from+" with message: '"+message+"'");
        CompletableFuture<String> result = new CompletableFuture<String>();
        result.complete("JavaAsync says: Hi "+from + ", nice to see you");
        return result;
    }
    @Override
    public Promise<String> sayHelloPromise(String from, String message) {
        System.out.println("Java.sayHelloPromise called by "+from+" with message: '"+message+"'");
        Deferred<String> deferred = new Deferred<String>();
        deferred.resolve("JavaPromise says: Hi "+from + ", nice to see you");
        return deferred.getPromise();
    }
}

The source with documentation is available here.

Note the service properties specified in the @Component annotation in the HelloImpl. The 'service.exported.interfaces=*' indicates that this service should be exported as a remote service. The 'service.exported.configs=ecf.py4j.host' indicates that it should be exported via the ECF Python.Java distribution provider. The 'osgi.basic.timeout=Long:50000' indicates that the remote method invocation for all methods in this interface should timeout after 50000ms/50s. The 'service.intents=osgi.async' indicates that the service methods with Promise and CompletableFuture (or Future or CompletionStage) should be considered Async Remote Services upon export. All of these service properties are specified by OSGi R7 Remote Services.

Even though the above implementation of sayHelloAsync and sayHelloPromise complete immediately (no blocking) when remote consumers import an instance of the IHello remote service (with the osgi.async service intent), the distribution provider will return a CompletableFuture (or Promise) immediately without blocking because of communication delays, and complete when the entire remote invocation is complete or fails...because of communication errors or impl errors.

Here is a Python consumer for this service


from pelix.ipopo.decorators import ComponentFactory,Instantiate,Requires,Validate
 
@ComponentFactory("remote-hello-consumer-factory")
# The '(service.imported=*)' filter only allows remote services to be injected
@Requires("_helloservice", "org.eclipse.ecf.examples.hello.IHello", False, False, "(service.imported=*)", False)
@Instantiate("remote-hello-consumer")
class RemoteHelloConsumer(object):
 
    def __init__(self):
        self._helloservice = None
        self._name = 'Python'
        self._msg = 'Hello Java'
 
    @Validate
    def _validate(self,bundle_context):
        # call it!
        resp = self._helloservice.sayHello(self._name+'Sync', self._msg)
        print("{0} IHello service consumer received sync response: {1}".format(self._name,resp))
        # call sayHelloAsync which returns Future and we add lambda to print the result when done
        self._helloservice.sayHelloAsync(self._name+'Async', self._msg).add_done_callback(lambda f: print('async response: {0}'.format(f.result())))
        print("done with sayHelloAsync method")
        # call sayHelloAsync which returns Future and we add lambda to print the result when done
        self._helloservice.sayHelloPromise(self._name+'Promise', self._msg).add_done_callback(lambda f: print('promise response: {0}'.format(f.result())))
        print("done with sayHelloPromise method")

This example uses ipopo/pelix and pelix.rsa packages to inject the IHello service proxy into the RemoteHelloConsumer instance...into the self._helloservice instance. The use of the @ComponentFactory, @Requires, @Instantiate, and @Validate decorators results in this behavior. See the ipopo project version 0.8.0 for more information. The result is that when the _validate method is called, the self._helloservice instance has been discovered and injected and so is ready for calling/use.

Note in _validate that the first call is to the self._helloservice.sayHello method. This method is a synchronous method, and so the calling Python thread will block until a result is returned (or the timeout results in exception).

Note that for both the sayHelloAsync and sayHelloPromise methods, that a Python **Future** is returned (i.e. concurrent.futures.Future). In both invocations a lambda is specified via Future.add_done_callback(func) for printing the result when the Future completes.

The Python.Java distribution provider takes care of the conversion between CompletableFuture and Future, and Promise -> Future in keeping with the contract of the OSGi Async Remote Services.

Running the Hello Example via Bndtools

With the ECF Photon/3.14.0 release, support has been added for using Bndtools to do remote service development. Before doing the below please use these instructions for setting up a workspace using the ECF/bndtools.workspace template.

1. Create an Empty Bndtools Project in the workspace (of any name).

2. Choose File->New->Run Descriptor File (.bndrun) and select the **Python.Java Hello Example**

Pyjavahello1.png

3. Choose Next> and give some filename for the .bndrun file

4. Open the created filename.bndrun file.

5. Click the **Resolve** button on the lower right of the bndrun editor underneath the Run Requirements list.

6. Click Finish on the resolve window presented.

7. Click the 'Debug OSGi' link in the upper right of the bndrun editor to start the example.

Once started, at the gogo console prompt give the following command to start the Java gateway listener:

osgi>scr:enable org.eclipse.ecf.examples.hello.provider.ExamplePy4jProvider

and

osgi>scr:enable org.eclipse.ecf.examples.hello.javahost.HelloImpl

to enable the HelloImpl component and export it via the ecf.py4j.host provider. This should output to the console the RSA endpoint description xml for the exported endpoint. See the Python.Java provider repo for more info

Running the Python Hello Consumer

TBD with ipopo 0.8.0

Running Hello Example in Karaf

Using Karaf 4.2+ see this page to install ECF Photon into Karaf.

1. Install the Python.Java distribution provider and the Hello example:

karaf@root()>feature:install -v ecf-rs-examples-python.java-hello

2. Enable the Python.Java distribution provider component

osgi>scr:enable org.eclipse.ecf.examples.hello.provider.ExamplePy4jProvider

3. Enable and export the HelloImpl service

osgi>scr:enable org.eclipse.ecf.examples.hello.javahost.HelloImpl

This should result in output to the console the RSA endpoint description xml for the exported endpoint. See the Python.Java provider repo for more info

Back to the top