Skip to main content

Notice: This Wiki is now read only and edits are no longer 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"

(Created page with "<!-- This CSS wraps code blocks in pretty boxes --> <css> .mw-code { background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-sty...")
 
 
(20 intermediate revisions 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].
 
+
OSGi Services are an excellent way to implement small, dynamic, component-based systems.  With features as service versioning, support for real dynamics, service injection (e.g. the service component runtime aka declarative services), service dependency-handling, a flexible broker, and a clear separation between service contract and implementation make it a valuable technology particularly for the Internet of Things. 
+
 
+
Traditionally, however, OSGi services have been both declared and implemented in Java.  With the OSGi Remote Services specification, and ECF's implementation of that [https://www.osgi.org/developer/specifications/ specification], it's now possible to create OSGi services implemented in Python and used/consumed in Java.  This tutorial shows a simple example of creating an OSGi service implemented in Python, that uses [https://developers.google.com/protocol-buffers/ Google Protocol Buffers] for performant cross-language serialization, and that automatically inherits all of the aspects of an OSGi service (dynamics, service injection, versioning, etc.).
+
 
+
==Defining a Simple 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 type was generated by the Protocol Buffers via a message declaration
+
 
+
<pre>
+
syntax = "proto3";
+
 
+
package org.eclipse.ecf.examples.protobuf.hello;
+
 
+
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 service declaration is complete.
+
 
+
==Python Implementation of IHello Service==
+
 
+
To implement this IHello service in Python it's necessary to provide a Python implementation class.  Here's an example:
+
 
+
<pre>
+
from osgiservicebridge.bridge import Py4jServiceBridge, _wait_for_sec
+
 
+
from osgiservicebridge.protobuf import protobuf_remote_service, protobuf_remote_service_method
+
from hellomsg_pb2 import HelloMsgContent
+
 
+
@protobuf_remote_service(objectClass=['org.eclipse.ecf.examples.protobuf.hello.IHello'])
+
class HelloServiceImpl:
+
   
+
    @protobuf_remote_service_method(arg_type=HelloMsgContent,return_type=HelloMsgContent)
+
    def sayHello(self,pbarg):
+
        print("sayHello called with arg="+str(pbarg))
+
        resmsg = HelloMsgContent()
+
        resmsg.h = 'Another response from Python'
+
        resmsg.f = 'frompython'
+
        resmsg.to = 'tojava'
+
        resmsg.hellomsg = 'Greetings from Python!!'
+
        for x in range(0,5):
+
            resmsg.x.append(float(x))
+
        return resmsg
+
   
+
   
+
if __name__ == '__main__':
+
    bridge = Py4jServiceBridge()
+
    print("bridge created")
+
    bridge.connect()
+
    print("bridge connected")
+
    hsid = bridge.export(HelloServiceImpl())
+
    print("exported")
+
    _wait_for_sec(5)
+
    bridge.unexport(hsid)
+
    print("unexported")
+
    bridge.disconnect()
+
    print("disconnected...exiting")
+
</pre>
+
 
+
Note a couple of things about the above Python 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 also 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 type. In this example they are both HelloMsgContent, but they may be of any type as long as it is a protocol buffers Message type.
+
#The implementation of '''__main__''' method creates and then connects a Py4jServiceBridge instance.  This
+
Py4jServiceBridge is responsible for exporting the service from Python to Java. 
+
 
+
The complete Python code is available [https://github.com/ECF/Py4j-RemoteServicesProvider/blob/master/examples/org.eclipse.ecf.examples.protobuf.hello/python-src/run.py here] in the org.eclipse.ecf.examples.protobuf.hello [https://github.com/ECF/Py4j-RemoteServicesProvider/tree/master/examples/org.eclipse.ecf.examples.protobuf.hello project].
+
 
+
The declaration and implementation complete this service, and so now it may be consumed as an OSGi service.
+
 
+
==Example Hello Consumer==
+
 
+
The example service consumer is available in [https://github.com/ECF/Py4j-RemoteServicesProvider/tree/master/examples/org.eclipse.ecf.examples.protobuf.hello.consumer this project]This consumer uses has the IHello service instance dynamically injected by Declarative Services and then it uses the service via this code (in activate method):
+
 
+
<pre>
+
HelloMsgContent.Builder b1 = HelloMsgContent.newBuilder();
+
b1.addX(1.1);
+
b1.addX(1.2);
+
b1.setF("fromjava");
+
b1.setTo("topython");
+
b1.setHellomsg("Hello message from java!");
+
b1.setH("An additional message from java");
+
HelloMsgContent request = b1.build();
+
HelloMsgContent result = this.helloService.sayHello(request);
+
System.out.println("Received result="+result);
+
 
+
</pre>
+
The second to last line makes the call from Java to Python via the '''IHello''' service proxy.
+
 
+
==The Java Gateway==
+
 
+
The only other piece required is a bundle to start the Java-side gateway, so that when the Python code is run it can connect to the Java gateway.  The Java Gateway is created and configured via [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 this component] in [https://github.com/ECF/Py4j-RemoteServicesProvider/tree/master/examples/org.eclipse.ecf.examples.protobuf.hello.provider this project].
+
 
+
==Running the Hello Example==
+
 
+
First, start the Java side (with the org.eclipse.ecf.examples.protobuf.hello bundle, org.eclipse.ecf.examples.protobuf.hello.consumer, and org.eclipse.ecf.examples.protobuf.hello.provider bundles).  If run within Eclipse you may use the protobufhello.javaconsumer.launch config (note that the target platform must be set first to this target:  releng/org.eclipse.ecf.provider.py4j.releng.target/ecf-oxygen.target).
+
 
+
Then the run.py should be started from Python.  Prior to starting the following Python libraries must be installed: '''protobuf version 3.2.0''', '''py4j version 0.10.6''', and the '''osgiservicebridge''' package located in [https://github.com/ECF/Py4j-RemoteServicesProvider/tree/master/python/osgiservicebridge this project].
+
 
+
Once the '''run.py''' is started and the Py4jServiceBridge is connected to the JavaGateway, the IHello proxy will be created, injected into the [https://github.com/ECF/Py4j-RemoteServicesProvider/blob/master/examples/org.eclipse.ecf.examples.protobuf.hello.consumer/src/org/eclipse/ecf/examples/protobuf/hello/consumer/HelloConsumer.java HelloConsumer class], and then the '''sayHello''' call will be made to Python->HelloServiceImpl->sayHello.
+
 
+
This IHello service is just an example.  Any other service could be similarly declared, implemented in Python, and injected and consumed in Java.
+
 
+
==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