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 "Tutorial: Python+Java for OSGi R7 Remote Services"

(Introduction)
 
(One intermediate revision by the same user not shown)
Line 72: Line 72:
 
</css>
 
</css>
  
==Introduction==
+
This tutorial has been deprecated in favor of the RSA for [http://ipopo.readthedocs.io iPOPO] implementation.  For a tutorial, [https://wiki.eclipse.org/OSGi_R7_Remote_Services_between_Python_and_Java see this wiki page].
 
+
[https://osgi.org/specification/osgi.cmpn/7.0.0/service.remoteservices.html OSGi R7 Remote Services] are an excellent way to construct small, dynamic, flexible, component-based distributed systems.  remote services inherit all the advantages of plain/local OSGi services...e.g. clear separation between service contract and implementation, as well as advanced features like service versioning, dynamics, service injection, instance dependency-handling and others, but remote services and [https://osgi.org/specification/osgi.cmpn/7.0.0/service.remoteserviceadmin.html Remote Service Admin (RSA)] add other valuable features such as standardized a management agent, pluggable distribution and discovery of remote services, standardized service intents, [https://osgi.org/specification/osgi.cmpn/7.0.0/service.remoteservices.html#d0e1407 asynchronous remote services], and flexibility in handling failure.
+
 
+
OSGi remote services have traditionally been declared, implemented and consumed via Java.  For example [https://wiki.eclipse.org/Using_the_Bndtools_Remote_Services_Project_Templates this tutorial].  However, with ECF's Photon RSA implementation, along with an RSA implementation contribution to the [http://ipopo.readthedocs.io iPopo 0.8.0 Python Framework], it's now possible to easily and flexibly export Java-based OSGi services to Python consumers, as well as export Python-implemented services to Java-based consumers.
+
 
+
This tutorial presents and describes how to execute a simple example OSGi R7 remote service implemented in Java and consumed by Python.  Then it presents the same service implemented in Python and consumed by Java.  These mechanisms can be easily used by developers to create any set of services across Java and Python environments.
+
 
+
==Declaring a Service==
+
 
+
Here is a simple example Java interface declaring a single 'sayHello' method:
+
 
+
<pre>
+
public interface IHello {
+
 
+
HelloMsgContent sayHello(HelloMsgContent message) throws Exception;
+
}
+
</pre>
+
 
+
This interface is declared [https://github.com/ECF/Py4j-RemoteServicesProvider/blob/master/examples/org.eclipse.ecf.examples.protobuf.hello/src/org/eclipse/ecf/examples/protobuf/hello/IHello.java here] in [https://github.com/ECF/Py4j-RemoteServicesProvider/tree/master/examples/org.eclipse.ecf.examples.protobuf.hello this project].
+
 
+
The HelloMsgContent class was generated by the Protocol Buffers protoc compiler based upon the following message declaration
+
 
+
<pre>
+
message HelloMsgContent {
+
string h = 1;
+
string f = 2;
+
string to = 3;
+
string hellomsg = 4;
+
repeated double x = 5;
+
}
+
</pre>
+
 
+
This message declaration, when run through the protoc compiler, generates the Java HelloMsgContent source code, and also generates a Python version of the same class.  The hellomsg.proto file is [https://github.com/ECF/Py4j-RemoteServicesProvider/blob/master/examples/org.eclipse.ecf.examples.protobuf.hello/hellomsg.proto here].
+
 
+
With the IHello interface and the HelloMsgContent class, the IHello service declaration is complete.
+
 
+
==Python Implementation==
+
 
+
To implement this IHello service in Python it's necessary to provide a Python implementation class.  Here's an example:
+
 
+
<pre>
+
@protobuf_remote_service(objectClass=['org.eclipse.ecf.examples.protobuf.hello.IHello'],export_properties = { ECF_SERVICE_EXPORTED_ASYNC_INTERFACES: 'org.eclipse.ecf.examples.protobuf.hello.IHello' })
+
class HelloServiceImpl:
+
   
+
    def __init__(self,props):
+
        self.props = props
+
       
+
    @protobuf_remote_service_method(arg_type=HelloMsgContent,return_type=HelloMsgContent)
+
    def sayHello(self,pbarg):
+
        print("sayHello called with arg="+str(pbarg))
+
        return create_hellomsgcontent('responding back to java hello ')
+
 
+
</pre>
+
 
+
Notes about this code:
+
 
+
#A HelloServiceImpl class that implements the '''sayHello''' method
+
#The HelloServiceImpl class has a decorator:
+
<pre>
+
@protobuf_remote_service(objectClass=['org.eclipse.ecf.examples.protobuf.hello.IHello'])
+
class HelloServiceImpl:
+
</pre>
+
associates the HelloServiceImpl Python class with the IHello Java service interface.
+
#The '''sayHello''' method has a decorator:
+
<pre>
+
    @protobuf_remote_service_method(arg_type=HelloMsgContent,return_type=HelloMsgContent)
+
    def sayHello(self,pbarg):
+
</pre>
+
indicates the types (Python class) of the pbarg type and the '''sayHello''' return typeIn this example they are both HelloMsgContent, but they may be of any type as long as it is a protocol buffers Message type.
+
#The __init__ method can be passed a dictionary (argument name='props') of string->string types to initialize the HelloServiceImpl instance.
+
 
+
The complete Python code is available [https://github.com/ECF/Py4j-RemoteServicesProvider/blob/master/examples/org.eclipse.ecf.examples.protobuf.hello.pythonhost/python-src/hello.py here].
+
 
+
===Accessing the hello Module===
+
 
+
To create an instance of HelloServiceImpl at runtime, some other python code has to be able to import the hello module.  Python 3 has support for creating an '''Import Hook''', that allows Java code (in this case an OSGi bundle) to resolve and import statement like this
+
 
+
<pre>
+
import hello
+
</pre>
+
 
+
This Py4j remote services provider now allows a '''Module Resolver''' service to be registered resulting in a Python 3 Import Hook to callback the Module Resolver to provide the hello module Python code.  In the org.eclipse.ecf.examples.protobuf.hello.pythonhost bundle, the [https://github.com/ECF/Py4j-RemoteServicesProvider/blob/master/bundles/org.eclipse.ecf.provider.direct/src/org/eclipse/ecf/provider/direct/ModuleResolver.java ModuleResolver] service impl is [https://github.com/ECF/Py4j-RemoteServicesProvider/blob/master/examples/org.eclipse.ecf.examples.protobuf.hello.pythonhost/src/org/eclipse/ecf/examples/protobuf/hello/pythonhost/PythonHostBundleModuleResolver.java PythonHostBundleModuleResolver].  This implementation reads the hello module source code from /python-src directory inside the bundle.  This happens only when the import hook is hit by the actual execution of the '''import hello''' python statement.
+
 
+
==IHello Service Consumer==
+
 
+
The service consumer (caller of the IHello service) is available in [https://github.com/ECF/Py4j-RemoteServicesProvider/tree/master/examples/org.eclipse.ecf.examples.protobuf.hello.consumer this project]. This consumer has the IHello service instance injected by Declarative Services, and then it calls the sayHello service instance in a separate thread:
+
 
+
<pre>
+
@Reference(policy=ReferencePolicy.DYNAMIC,cardinality=ReferenceCardinality.OPTIONAL,target="(service.imported=*)")
+
void bindHello(IHello hello) {
+
this.helloService = hello;
+
new Thread(new Runnable() {
+
@Override
+
public void run() {
+
try {
+
HelloMsgContent result = helloService.sayHello(createRequest());
+
System.out.println("Java received sayHello result="+result);
+
} catch (Exception e) {
+
e.printStackTrace();
+
}
+
}}).start();
+
}
+
</pre>
+
 
+
This is the line that actually makes the remote call of the Python-implemented remote service:
+
 
+
<pre>
+
...
+
HelloMsgContent result = helloService.sayHello(createRequest());
+
...
+
</pre>
+
 
+
This call results in the synchronous execution of the python code in HelloServiceImpl
+
 
+
<pre>
+
    def sayHello(self,pbarg):
+
        print("sayHello called with arg="+str(pbarg))
+
        return create_hellomsgcontent('responding back to java hello ')
+
</pre>
+
 
+
==Running the Example==
+
 
+
The following example bundles and their dependencies (in examples module) must be present in an Eclipse workspace to run this example:
+
 
+
<pre>
+
org.eclipse.ecf.examples.protobuf.hello
+
org.eclipse.ecf.examples.protobuf.hello.consumer
+
org.eclipse.ecf.examples.protobuf.hello.javahost
+
org.eclipse.ecf.examples.protobuf.hello.provider
+
org.eclipse.ecf.examples.protobuf.hello.pythonhost
+
</pre>
+
 
+
Once in workspace, the launch config examples/org.eclipse.ecf.examples.protobuf.hello.consumer/protobufhello.java.launch can be used to launch the example from the Eclipse Run/Debug Configurations dialog.  The [https://github.com/ECF/Py4j-RemoteServicesProvider/blob/master/examples/org.eclipse.ecf.examples.protobuf.hello.provider org.eclipse.ecf.examples.protobuf.hello.provider] bundle is responsible for
+
 
+
# Launching Python 3 via the [https://github.com/ECF/Py4j-RemoteServicesProvider/blob/master/examples/org.eclipse.ecf.examples.protobuf.hello.provider/src/org/eclipse/ecf/examples/protobuf/hello/provider/ExampleProtobufPythonLauncher.java ExampleProtobufPythonLauncher] and the [https://github.com/ECF/Py4j-RemoteServicesProvider/blob/master/examples/org.eclipse.ecf.examples.protobuf.hello.provider/src/org/eclipse/ecf/examples/protobuf/hello/provider/ExampleProtobufPythonLaunchCommandProvider.java ExampleProtobufPythonLaunchCommandProvider] python command.
+
# Connect the Python process to the Java process via the [https://github.com/ECF/Py4j-RemoteServicesProvider/blob/master/examples/org.eclipse.ecf.examples.protobuf.hello.provider/src/org/eclipse/ecf/examples/protobuf/hello/provider/ExampleProtobufPy4jProvider.java ExampleProtobufPy4jProvider] distribution provider
+
 
+
==Background and Related Articles==
+
 
+
[[Tutorial:_Creating_Custom_Distribution_Providers]]
+
 
+
[[Getting Started with ECF's OSGi Remote Services Implementation]]
+
 
+
[[OSGi Remote Services and ECF]]
+
 
+
[[Asynchronous Proxies for Remote Services]]
+
 
+
[[File-based Discovery | Static File-based Discovery of Remote Service Endpoints]]
+
 
+
[[EIG:Download|Download ECF Remote Services/RSA Implementation]]
+
 
+
[[EIG:Add to Target Platform|How to Add Remote Services/RSA to Your Target Platform]]
+

Latest revision as of 18:54, 25 July 2018


This tutorial has been deprecated in favor of the RSA for iPOPO implementation. For a tutorial, see this wiki page.

Back to the top