This is a part of dCache.ORG's NFSv4.1 work.
Technically, this is not a fork of Remote Tea RPC library, but formally it is as we was inspired by Remote Tea RPC and took lot of ideas from it including xdr language parser. The goal to be able to use stubs generated by Remote Tea (no need to rewrite existing RPC servers) with minimal changes.
The library supports IPv6, RPCSEC_GSS and compliant with rfc1831 and rfc2203.
packageme.mypackage; importorg.dcache.oncrpc4j.rpc.OncRpcException; importorg.dcache.oncrpc4j.rpc.RpcDispatchable; importorg.dcache.oncrpc4j.rpc.RpcCall; importorg.dcache.oncrpc4j.xdr.XdrVoid; publicclassSvcd { privatestaticfinalintDEFAULT_PORT = 1717; privatestaticfinalintPROG_NUMBER = 111017; privatestaticfinalintPROG_VERS = 1; publicstaticvoidmain(String[] args) throwsException { intport = DEFAULT_PORT; RpcDispatchabledummy = newRpcDispatchable() { @OverridepublicvoiddispatchOncRpcCall(RpcCallcall) throwsOncRpcException, IOException { call.reply(XdrVoid.XDR_VOID); } }; OncRpcSvcservice = newOncRpcSvcBuilder() .withTCP() .withAutoPublish() .withPort(port) .withSameThreadIoStrategy() .withRpcService(newOncRpcProgram(PROG_NUMBER, PROG_VERS), dummy) .build(); service.start(); } }
packageme.mypackage; importorg.dcache.oncrpc4j.rpc.OncRpcException; importorg.dcache.oncrpc4j.rpc.RpcDispatchable; importorg.dcache.oncrpc4j.rpc.RpcCall; importorg.dcache.oncrpc4j.xdr.XdrVoid; importjava.io.IOException; publicclassSvcdimplementsRpcDispatchable { @OverridepublicvoiddispatchOncRpcCall(RpcCallcall) throwsOncRpcException, IOException { call.reply(XdrVoid.XDR_VOID); } }
<?xml version="1.0" encoding="UTF-8"?> <beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <beanid="my-rpc-svc"class="me.mypackage.Svcd"> <description>My RPC service</description> </bean> <beanid="my-rpc"class="org.dcache.oncrpc4j.rpc.OncRpcProgram"> <description>My RPC program number</description> <constructor-argindex="0"value="1110001" /> <constructor-argindex="1"value="1" /> </bean> <beanid="rpcsvc-builder"class="org.dcache.oncrpc4j.rpc.OncRpcSvcFactoryBean"> <description>Onc RPC service builder</description> <propertyname="port"value="1717"/> <propertyname="useTCP"value="true"/> <propertyname="rpcServices"> <map> <entrykey-ref="my-rpc"value-ref="my-rpc-svc"/> </map> </property> </bean> <beanid="oncrpcsvc"class="org.dcache.oncrpc4j.rpc.OncRpcSvc"init-method="start"destroy-method="stop"> <description>My RPC service</description> <constructor-argref="rpcsvc-builder"/> </bean> </beans>
Notice, that included SpringRunner will try to instantiate and run bean with id oncrpcsvc.
java -cp $CLASSPATH org.dcache.oncrpc4j.spring.SpringRunner svc.xml
With version 3.0.0 a new package schema is introduced. As the change is not backward compatible with older version some minimal code changes are required.
org.dcache.utils.Bytes#{fromHexString|toHexString} methods are removed in favour of com.google.common.io.BaseEncoding.
org.dcache.utils => org.dcache.oncrpc4j.util org.dcache.utils.net => org.dcache.oncrpc4j.rpc.net org.dcache.xdr split into org.dcache.oncrpc4j.rpc, org.dcache.oncrpc4j.xdr and org.dcache.oncrpc4j.grizzly
org.dcache.utils.Opaque => into org.dcache.oncrpc4j.XdrOpaque org.dcache.xdr.XdrTransport => into org.dcache.oncrpc4j.rpc.RpcTransport org.dcache.xdr.GrizzlyXdrTransport => into org.dcache.oncrpc4j.grizzly.GrizzlyRpcTransport
org.dcache.xdr.XdrBuffer is removed. Use org.dcache.oncrpc4j.xdr.Xdr.
The Xdr#xdrEncodeByteBuffer changed to not flip provided byte buffer. As a result, the Xdr#xdrEncodeByteBuffer will encode data in the buffer from buffers current position up to the limit:
ButeByfferbuffer = ...; Xdrxdr = ...; buffer.put(...); buffer.flip(); xdr.xdrEncodeByteBuffer(buffer);
Assume a service which calculates a the length of a string. It provides a single remote call strlen which takes a string as an argument ans returns it's length. Let describe that procedure according XDR language specification:
/* strlen.x */programSTRLEN { versionSTRLENVERS { intstrlen(string) =1; } =1; } =117;
Here we define STRLEN program number to be 117 and version number 1. Now we can generate stub files for client and server:
java -jar oncrpc4j-rpcgen.jar -c StrlenClient strlen.x
Simply extend this class and implement abstract methods:
//StrlenSvcImpl.javaimportorg.dcache.oncrpc4j.rpc.*; importorg.dcache.oncrpc4j.xdr.*; publicclassStrlenSvcImplextendsstrlenServerStub { publicintstrlen_1(RpcCallcall$, Stringarg) { returnarg.length(); } }
Now it's ready to be complied and deployed as standalone or Spring application:
// StrlenServerApp.java// standalone application exampleimportorg.dcache.oncrpc4j.rpc.*; importorg.dcache.oncrpc4j.xdr.*; publicclassStrlenServerApp { staticfinalintDEFAULT_PORT = 1717; publicstaticvoidmain(String[] args) throwsException { OncRpcSvcservice = newOncRpcSvcBuilder() .withTCP() .withAutoPublish() .withPort(DEFAULT_PORT) .withSameThreadIoStrategy() .withRpcService(newOncRpcProgram(strlen.STRLEN, strlen.STRLENVERS), newStrlenSvcImpl()) .build(); service.start(); System.in.read(); } }
In addition, a client will be generated as well which can be used as:
// StrlenClientApp.javaimportjava.net.InetAddress; importorg.dcache.oncrpc4j.rpc.*; importorg.dcache.oncrpc4j.xdr.*; publicclassStrlenClientApp { staticfinalintDEFAULT_PORT = 1717; publicstaticvoidmain(String[] args) throwsException { InetAddressaddress = InetAddress.getByName(args[0]); StrlenClientclient = newStrlenClient(address, DEFAULT_PORT, strlen.STRLEN, strlen.STRLENVERS, IpProtocolType.TCP); System.out.println("Length of " + args[1] + " = " + client.strlen_1(args[1])); client.shutdown(); } }
Your RPC client and server are ready!
<dependency> <groupId>org.dcache</groupId> <artifactId>oncrpc4j-core</artifactId> <version>3.0.2</version> </dependency> <repositories> <repository> <id>dcache-snapshots</id> <name>dCache.ORG maven repository</name> <url>https://download.dcache.org/nexus/content/repositories/releases</url> <layout>default</layout> </repository> </repositories>
In some situation, OncRpcSvc can internally call other services which require client subject to be set in the context of the current thread. We use standard Java's Subject.doAs() mechanism to inject user subject into processing thread. As a result, the user subject can be extracted from AccessControlContext.
// SomeService.javaimportjavax.security.auth.Subject; importjava.security.AccessController; publicclassSomeService { publicvoiddoSomeTask() { Subjectsubject = Subject.getSubject(AccessController.getContext()); // start using subject } } // SubjectAvareSvcImpl.javapublicclassSubjectAvareSvcImplimplementsRpcDispatchable { @OverridepublicvoiddispatchOncRpcCall(RpcCallcall) throwsOncRpcException, IOException { externalService.doSomeTask(); call.reply(XdrVoid.XDR_VOID); } }
To avoid unnecessary overhead, subject propagation is not enabled by default:
OncRpcSvcservice = newOncRpcSvcBuilder() .withTCP() .withAutoPublish() .withSameThreadIoStrategy() .withRpcService(... , newSubjectAvareSvcImpl()) .withSubjectPropagation() .build();
oncrpc4j uses Grizzly NIO framework which comes with it's own JMX monitoring capabilities. To enable it just add grizzly-framework-monitoring
jar with it's dependencies into the application's classpath. See Grizzly framework dependencies for the instructions.
With the provided stable automatic module name org.dcache.oncrpc4j, oncrpc4j can be used in modular java9 application:
modulecom.foo.bar { requiresorg.dcache.oncrpc4j; }
oncrpc4j supports rpc-over-tls IETF activity. The goal of the project is to protect in-transit Remote Procedure Call messages with TLS. To enable RPC-over-TLS:
SSLContextsslServerContext = ...; svc = newOncRpcSvcBuilder() .withTCP() .... .withSSLContext(sslServerContext) .withStartTLS() .withServiceName("svc") .build(); svc.start();
or, if special SSLParameters
configuration is required, like cipher types, then:
SSLContextsslServerContext = ...; SSLParametersparameters = ...; svc = newOncRpcSvcBuilder() .withTCP() .... .withSSLContext(sslServerContext) .withSSLParameters(parameters) .withStartTLS() .withServiceName("svc") .build(); svc.start();
oncrpc4j uses the linux kernel model of using git not only a source repository, but also as a way to track contributions and copyrights.
Each submitted patch must have a "Signed-off-by" line. Patches without this line will not be accepted.
The sign-off is a simple line at the end of the explanation for the patch, which certifies that you wrote it or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify the below:
Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved.
then you just add a line saying ( git commit -s )
Signed-off-by: Random J Developer <random@developer.example.org>
using your real name (sorry, no pseudonyms or anonymous contributions.)