This project implements a tiny RPC protocol over TCP connections using Protobuffer as a serialization mechanism.
It also contains a implementation of OSGi RSA (Remote Service Admin)
DistributionProvider
, which allows any OSGi service to be exported as a remote service
via the Apache Aries RSA implementation.
The RPC protocol is as simple as it can be and consists of the single rpc.proto file:
syntax = "proto3";
package com.athaydes.protobuf.tcp.api;
import "google/protobuf/any.proto";
option java_package = "com.athaydes.protobuf.tcp.api";
option java_outer_classname = "Api";
message MethodInvocation {
string methodName = 1;
repeated google.protobuf.Any args = 2;
}
message Exception {
string type = 1;
string message = 5;
}
message Result {
oneof result {
google.protobuf.Any successResult = 1;
Exception exception = 2;
}
}
This protocol can represent any method invocation and can be used from any language that supports Protobuffers.
The following example shows how to start a remote service server and client in Java:
// This code can be run with JGrab: https://github.com/renatoathaydes/jgrab
// #jgrab com.athaydes.osgi:protobuf-tcp-rsa-provider:0.1.0
// #jgrab org.slf4j:slf4j-simple:1.7.25
import com.athaydes.protobuf.tcp.api.RemoteServices;
import java.io.Closeable;
import java.io.IOException;
import java.util.function.IntToDoubleFunction;
public class HelloWorld {
public static void main(String[] args) throws IOException {
// service implementation
IntToDoubleFunction squareCalculator = (n) -> Math.pow(n, 2);
// create server
Closeable server = RemoteServices.provideService(squareCalculator, 8023, IntToDoubleFunction.class);
// create client
IntToDoubleFunction client = RemoteServices.createClient(IntToDoubleFunction.class, "localhost", 8023);
try {
// invoke the remote service
double response = client.applyAsDouble(5);
System.out.println("The square of 5 is " + response);
} finally {
server.close();
}
}
}
Other Java examples can be found in the samples/java directory.
Check the samples directory for examples in other languages.
To use this bundle in OSGi, you'll need to install the Aries RSA runtime bundles.
There is an Aries RSA Example project that uses Gradle and the osgi-run plugin to create a runtime.
The OSGi runtime declarations look like this:
dependencies {
// Aries RSA bundles
final ariesRsaVersion = '1.11.0'
osgiRuntime "org.apache.aries.rsa:org.apache.aries.rsa.core:$ariesRsaVersion"
osgiRuntime "org.apache.aries.rsa:org.apache.aries.rsa.spi:$ariesRsaVersion"
// set topology-manager's startLevel to 6 to make sure it starts AFTER the RSA Core,
// otherwise it will not work
osgiRuntime osgi("org.apache.aries.rsa:org.apache.aries.rsa.topology-manager:$ariesRsaVersion:6".toString())
osgiRuntime "org.apache.aries.rsa.discovery:org.apache.aries.rsa.discovery.local:$ariesRsaVersion"
osgiRuntime "com.athaydes.osgi:protobuf-tcp-rsa-provider:1.0-SNAPSHOT"
// Logging
osgiRuntime 'org.apache.felix:org.apache.felix.log:1.0.1', {
transitive = false
}
osgiRuntime 'com.athaydes.osgiaas:slf4j-to-osgi-log:1.7.0'
}
To export a service remotely, you just need to declare one service property:
service.exported.interfaces=*
You can also set the port to which the service is bound by declaring the following property
(in this case, setting the port to 5561
):
com.athaydes.protobuf.port=5561
Hence, using Declarative Services annotations, you would annotate your service with the following:
@Component(immediate = true, property = {
"service.exported.interfaces=*",
"com.athaydes.protobuf.port=5561"
})
public class MyService implements SomeService {}
To import a remote OSGi service, you must declare at least the following property:
<property name="service.imported.configs">com.athaydes.protobuf</property>
If you are using Local Discovery, you can create a XML descriptor like the following in order to import a remote service:
<endpoint-descriptions xmlns="http://www.osgi.org/xmlns/rsa/v1.0.0">
<endpoint-description>
<property name="objectClass">
<array>
<value>com.athaydes.osgi.api.MessageService</value>
</array>
</property>
<property name="endpoint.id">tcp://127.0.0.1:5561</property>
<property name="service.imported.configs">com.athaydes.protobuf</property>
</endpoint-description>
</endpoint-descriptions>
See the XML Descriptor in the Aries RSA Example for details.