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

OSGi R7 Remote Services between Python and Java

Revision as of 17:28, 15 June 2018 by Slewis.composent.com (Talk | contribs) (Created page with "This tutorial shows the use of[https://osgi.org/specification/osgi.cmpn/7.0.0/service.remoteservices.html Remote Services] and [https://travis-ci.org/ECF/Py4j-RemoteServicesPr...")

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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 Asynchronous Remote Services between Python and Java. This is also shown below.

Hello Service Interface

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

package org.eclipse.ecf.examples.hello;
 
import java.util.concurrent.CompletableFuture;
 
import org.osgi.util.promise.Promise;
 
/**
 * Example remote service interface.  Has async methods (CompletableFuture<String> and Promise<String>
 * return types), as defined by the 
 * <a href="https://osgi.org/specification/osgi.cmpn/7.0.0/service.remoteservices.html#d0e1407">Asynchronous Remote Services</a>
 * specification.   As per the intent, when invoked remotely, these methods will immediately
 * return a CompletableFuture or Promise that will subsequently return a result of declared type (String in below).
 * If the osgi.basic.timeout service property is set, the CompletableFuture or promise will timeout
 * after the given milliseconds.
 *
 */
public interface IHello {
 
	/**
	 * Synchronously provide 'from' and 'message' Strings to service, and receive a
	 * String response.
	 *  
	 * @param from a String identifying the sender
	 * @param message a String message
	 * @return String response from remote service after synchronously invoking remote method
	 */
	String sayHello(String from, String message);
 
	/**
	 * Asynchronously invoke remote method and provide String result via returned
	 * CompletableFuture.
	 * 
	 * @param from a String identifying the sender
	 * @param message a String message
	 * @return CompletableFuture that invokes remote method asynchronously to complete.  
	 * Will throw java.util.concurrent.TimeoutException if response is not
	 * received in within timeout provided via osgi.basic.timeout service prop on imple
	 */
	CompletableFuture<String> sayHelloAsync(String from, String message);
 
	/**
	 * Asynchronously invoke remote method and provide String result via returned 
	 * Promise.
	 * 
	 * @param from a String identifying the sender
	 * @param message a String message
	 * @return Promis that invokes remote method asynchronously to complete.  
	 * Will throw java.util.concurrent.TimeoutException if response is not
	 * received in within timeout provided via osgi.basic.timeout service prop on impl
	 */
	Promise<String> sayHelloPromise(String from, String message);
 
}

==Python.Java Services


Once you have created a Workspace using the Remote Services template, it's now possible to use Bndtools project templates to create a new remote service API project, create an implementation of this remote service, create a consumer of the remote service, and run/test the implementation and consumer.

Create a Remote Service API Project

In the new workspace there is a Remote Services API Project template to help create a Remote Service API project. To use this template, select File->New->Bnd OSGi Project

Bndtools.2.png

Select Remote Service API Project->Next

Project Name: org.example.remoteservice.api

Note: this is a suggested name for the example project, when you use this template for creating your own remote service you can specify an appropriate project/bundle/package name.

Choose Next->Finish. This will create a new project in your workspace named org.example.remoteservice.api

Create a Remote Service Impl Project

Select File->Next->Bnd OSGi Project->Remote Service Impl Project template

Project Name: org.example.remoteservice.impl

Select Next. Under Template Parameters for the apipackage parameter specify the name of the API project created above (e.g. org.example.remoteservice.api) and Next->Finish.

Create a Remote Service Consumer Project

Similar to the Impl project above, create a consumer project by selecting the Remote Service Consumer Project template.

Once all three (API, Impl, Consumer) projects are created, the workspace should look something like this

Bndtools.3.png

Create and Use Bnd Run Descriptor for Impl and Consumer

In the Package Explorer select the impl project (e.g. org.example.remoteservice.impl), and then File->New->Run Descriptor File (.bndrun). Select the ECF Generic Distribution Provider template->Next, and give an appropriate File Name...e.g. impl and Finish. Click Resolve under the Run Requirements section and then choose the Run OSGi or Debug OSGi icons in the upper right of the Resolve/Run. This should result in a Console opening and output similar to the following in the console

____________________________
Welcome to Apache Felix Gogo

g! 14:05:38.659;EXPORT_REGISTRATION;exportedSR=[org.example.remoteservice.api.ExampleRemoteService];cID=StringID[ecftcp://slewis-lenovo:54390/server];rsId=1
--Endpoint Description---
<endpoint-descriptions xmlns="http://www.osgi.org/xmlns/rsa/v1.0.0">
  <endpoint-description>
    <property name="ecf.endpoint.id" value-type="String" value="ecftcp://slewis-lenovo:54390/server"/>
    <property name="ecf.endpoint.id.ns" value-type="String" value="org.eclipse.ecf.core.identity.StringID"/>
    <property name="ecf.endpoint.ts" value-type="Long" value="1524171938628"/>
    <property name="ecf.rsvc.id" value-type="Long" value="1"/>
    <property name="endpoint.framework.uuid" value-type="String" value="45a4a861-3487-4089-a940-be622466a7bc"/>
    <property name="endpoint.id" value-type="String" value="937b3136-0864-4e3b-bf87-0462502eb1a5"/>
    <property name="endpoint.package.version.org.example.remoteservice.api" value-type="String" value="1.0.0"/>
    <property name="endpoint.service.id" value-type="Long" value="64"/>
    <property name="objectClass" value-type="String">
      <array>
        <value>org.example.remoteservice.api.ExampleRemoteService</value>
      </array>
    </property>
    <property name="remote.configs.supported" value-type="String">
      <array>
        <value>ecf.generic.server</value>
      </array>
    </property>
    <property name="remote.intents.supported" value-type="String">
      <array>
        <value>osgi.basic</value>
        <value>osgi.async</value>
        <value>osgi.private</value>
        <value>passByValue</value>
        <value>exactlyOnce</value>
        <value>ordered</value>
      </array>
    </property>
    <property name="service.imported" value-type="String" value="true"/>
    <property name="service.imported.configs" value-type="String">
      <array>
        <value>ecf.generic.server</value>
      </array>
    </property>
    <property name="service.intents" value-type="String" value="osgi.basic"/>
  </endpoint-description>
</endpoint-descriptions>
---End Endpoint Description

This debug output indicates that the ExampleRemoteServiceImpl was exported by the ECF RSA implementation and is ready to be used by a consumer.

Consumer

Similar to the Impl described above, create a Bnd Run Descriptor for the consumer project, then resolve and Run/Debug OSGi.

If the Zeroconf discovery is enabled on your LAN, after a few seconds the following output should appear on the consumer's console

Welcome to Apache Felix Gogo

g! service responds=Hello ExampleRemoteServiceConsumer
14:08:52.185;IMPORT_REGISTRATION;importedSR=[org.example.remoteservice.api.ExampleRemoteService];cID=StringID[ecftcp://slewis-lenovo:54390/server];rsId=1
--Endpoint Description---
<endpoint-descriptions xmlns="http://www.osgi.org/xmlns/rsa/v1.0.0">
  <endpoint-description>
    <property name="ecf.endpoint.id" value-type="String" value="ecftcp://slewis-lenovo:54390/server"/>
    <property name="ecf.endpoint.id.ns" value-type="String" value="org.eclipse.ecf.core.identity.StringID"/>
    <property name="ecf.endpoint.ts" value-type="Long" value="1524171938628"/>
    <property name="ecf.rsvc.id" value-type="Long" value="1"/>
    <property name="endpoint.framework.uuid" value-type="String" value="45a4a861-3487-4089-a940-be622466a7bc"/>
    <property name="endpoint.id" value-type="String" value="937b3136-0864-4e3b-bf87-0462502eb1a5"/>
    <property name="endpoint.package.version.org.example.remoteservice.api" value-type="String" value="1.0.0"/>
    <property name="endpoint.service.id" value-type="Long" value="64"/>
    <property name="objectClass" value-type="String">
      <array>
        <value>org.example.remoteservice.api.ExampleRemoteService</value>
      </array>
    </property>
    <property name="remote.configs.supported" value-type="String">
      <array>
        <value>ecf.generic.server</value>
      </array>
    </property>
    <property name="remote.intents.supported" value-type="String">
      <array>
        <value>osgi.basic</value>
        <value>osgi.async</value>
        <value>osgi.private</value>
        <value>passByValue</value>
        <value>exactlyOnce</value>
        <value>ordered</value>
      </array>
    </property>
    <property name="service.imported.configs" value-type="String">
      <array>
        <value>ecf.generic.client</value>
      </array>
    </property>
    <property name="service.intents" value-type="String" value="osgi.basic"/>
  </endpoint-description>
</endpoint-descriptions>
---End Endpoint Description

Note the line in the output:

g! service responds=Hello ExampleRemoteServiceConsumer

This indicates that the remote service was discovered, the endpoint description imported, and the proxy injected into the ExampleRemoteServiceConsumer. Then the ExampleRemoteServiceConsumer.activated method was called by DS, which is implemented with a call to the ExampleRemoteService.hello remote service method.

Breakpoints can be set in the Impl, and/or Consumer projects to inspect what's happening at runtime. The example code can also be refactored/renamed/modified.

Back to the top