FEPCOS-J: Implementing a Java Server and a Rust Client without Manual Network Programming
October 04, 2024This is a result of the FEPCOS-Project, aka @FepcosInfo; #FepcosJ_blog6.
FEPCOS-J allows declaratively implementing a Java server and generating its profile, which, for example, can be used to generate the networking code of a Rust client.
Introduction
As I have shown in a previous post [1] and a video [2], the FEPCOS-J development tool [3] prototypically implements a Java language extension that allows implementing a client-server application in Java without the need for manual network programming.
In a nutshell, FEPCOS-J allows a Java developer to program a server's system specification declaratively using annotations. The FEPCOS-J Processor fjp uses this specification to generate the server's system export module and system import module.
To explain, combined with the FEPCOS-J Exporter fjx, the system export module is a multithreaded Java server that provides its services via an IPv4 network. Further, the system import module contains a system interface that allows Java-programmed clients to access the server via the network.
The key feature of FEPCOS-J is that it enables client-server communication by supplying Java modules as well as generating Java code. In other words, it frees a developer from network programming. See my other posts [4,5,6] for details.
So far, I pointed out FEPCOS-J's capability to support the implementation of a Java server accessed via a Java client. However, FEPCOS-J is capable of more!
As FEPCOS-J is model-based [7] and employs profiling techniques researched in the fields of autonomous services [8,9] and wireless sensor networks [10], it is possible to use FEPCOS-J to implement a Java server and generate its system profile.
Other tools can then use this profile as input to realize the client-network communication for other programming languages, such as Rust [11].
This post illustrates this concept using the example of a Java server accessed by a Rust client. Firstly, the post describes the principle. Then, it provides an example. Finally, it draws a conclusion.
Java server and Rust client implemented using tools from the FEPCOS-Project
This section describes how to implement a Java server and a Rust client without manually network programming (Fig. 1).
For this purpose, a developer uses FEPCOS-J and the working draft of FEPCOS-R, which so far consists of a Rust library and the frg tool (FEPCOS-R Generator).
The developer:
- firstly uses the FEPCOS-J annotations to implement the server's system specification in Java.
- secondly runs
fjp --profile
, which generates the server's system export module (server.exp, a modular Jar file) and the server's system profile. - thirdly runs
frg
to generate the server's system import module (server_imp), a Rust library. - fourthly implements the client using the Rust programming language.
- fifthly uses
rustc
to compile the Rust client into an executable binary.
To start the Java server, the developer runs fjx
utilizing server.exp. After that, the developer can run the Rust client's executable binary.
As a result, the Rust client accesses the Java server via the frg-generated server_imp Rust library, the Rust library provided by FEPOCS-R, the network, the Java modules provided by FEPCOS-J, and the fjp-generated server.exp Java module.
Java server and Rust client implemented as examples
The following describes the implementation of the example scenario shown in Fig. 2: In brief, a Java server called test.server provides two services called add() and greet() via the internet socket 10.0.0.6:8888, and a Rust client called test_client accesses these services via the network.
In other words, I explain the server's programming using the Java programming language and the client's programming using the Rust programming language.
With this in mind, the interaction of the tools from the FEPCOS-Project becomes obvious.
Implementing the Java server
Workflow
To implement the Java server, the developer uses FEPCOS-J as illustrated in Fig. 3:
Firstly, the developer creates the project directory test.server with all sub folders and changes into the project directory.
Secondly, the developer uses FEPCOS-J's annotations to program the server in Java.
Finally, the developer runs
fjp --profile
.
As a result, the tgt directory contains:
- the server's system profile,
test.server.fsp
; - the server's system export module,
test.server.exp.jar
; - the server's system import module,
test.server.imp.jar
; - the server's system documentation
test.server.imp-doc.zip
.
As I have shown in my previous post [1], the test.server.imp.jar and test-server.imp-doc.zip files could be used to implement a Java client. But as this post focuses on programming a Rust client, it ignores these files in the following.
Source code of the Java server
The developer uses Java and FEPCOS-J's annotations to program the server's system specification, which consists of the source code listed below:
test.server/ └── src ├── module-info.java └── test └── server ├── AddService.java ├── GreetService.java └── Server.java 4 directories, 4 files
The following briefly explains the example's source code.
module test.server { requires static fepcos.j.annotation; }
module-info.java
is the module descriptor. It specifies the server's name, test.server.
package test.server; import fepcos.j.annotation.*; @SYDec("A simple server.") public class Server { @Cap AddService add; @Cap GreetService greet; }
Server.java
is the server's system declaration. It declares the add() and greet() services to be implemented as AddService class or GreetService class, respectively.
package test.server; import fepcos.j.annotation.*; @AYSpec("Adds two numbers.") class AddService { @In("1st summand") int x; @In("2nd summand") int y; @Out("The sum z=x+y") int z; @Behavior void go() { z = x+y; } }
AddService.java
is the add() service's activity specification. It specifies the input parameters, int x and int y. Further, it specifies the output parameter, int z, to be calculated as z=x+y
.
package test.server; import fepcos.j.annotation.*; @AYSpec("Greets the user in English and German.") class GreetService { @In("Name of the user") String user; @Out("Greetings in English.") String greet_en; @Out("Greetings in German.") String greet_de; @Behavior void go() { greet_en = "Hello, " + user + "!"; greet_de = "Hallo " + user + "!"; } }
GreetService.java
is the greet() service's activity specification.
It specifies the input parameter, String user.
Further, it specifies the output parameters, String greet_en, and String greet_de. The first becomes the English greet: greet_en = "Hello, " + user + "!"
. The second becomes the German greet: greet_de = "Hallo " + user + "!"
.
Implementing the Rust client
Workflow
Fig. 4 depicts the implementation of the Rust client, which accesses the Java server implemented as shown above via the network.
The developer:
- firstly creates the project directory test_client and changes into it.
- secondly runs scp to fetch the server's system profile, test.server.fsp, from the server.
- thirdly runs frg to process test.server.fsp and generate the Rust source code (test_server_imp.rs) and the Rust library (libtest_server_imp.rlib) of the server's system import module.
- fourthly implements the client test_client.rs in Rust using libtest_server_imp.rlib.
- finally used rustc to compile test_client.rs into the executable binary test_client.
Source code of the Rust client
extern crate test_server_imp; use test_server_imp::S; fn main() { let sy : S = S{ addr: "10.0.0.6:8888" }; let r1 = sy.add(14, 23).unwrap(); println!("{:?}", r1.z); let r2 = sy.greet(String::from("Bob")).unwrap(); println!("{:?}", r2.greet_en); println!("{:?}", r2.greet_de); }
To implement the Rust client, the developer requires the previously generated
extern crate test_server_imp;
to
use test_server_imp::S;
which is the server's system interface.
Within the main function fn main() { … }
, the line
let sy : S = S{ addr: "10.0.0.6:8888" };
firstly instantiates this system interface, specifying the socket address 10.0.0.6:8888
at which the server listens for incoming requests. Next,
let r1 = sy.add(14, 23).unwrap();
requests the server's add()
service, specifying the input parameters x=14
and y=23
. The result is stored in the variable r1
, a struct with the filed r1.z
, the service's z output parameter, printed afterwards. Finally,
let r2 = sy.greet(String::from("Bob")).unwrap();
requests the server's greet()
service, specifying the input parameter user="Bob"
. The result is stored in the variable r2
, a struct with the r2.greet_en
and r2.greet_de
fields. These fields represent the corresponding output parameters of the service and are then printed.
Executing the client-server application
First, the developer starts the Java server on a computer named lumo by executing
fjx tgt test.server.exp 10.0.0.6:8888
in the project directory, as shown in Fig. 5.
To explain, fjx is the FEPCOS-J Exporter, tgt is the directory containing the generated server's system export module, test.server.exp is the name of the system export module, and 10.0.0.6:8888 is the internet socket at which the server listens for incoming requests.
After that, the developer runs the Rust client on a computer named box by executing the rustc-built test_client binary, as shown in Fig. 6.
The test_client binary prints 37 (=14 + 23), "Hello, Bob!" and "Hallo Bob!" as expected.
Java server and Rust client can be implemented without manually network programming
To sum up, this post introduced FEPCOS-J's feature to generate a system profile out of a Java server's system specification. In addition, the post introduced the working draft of FEPCOS-R, which can process the system profile and generate the system import module for a Rust client.
As an illustration, the post gave an example using FEPCOS-J for programming a Java server that provides two services. The fjp tool processed the Java code and generated the server's system profile.
Next, the frg tool processed this profile and generated a Rust library. This library contained the server's system interface for a Rust client programmed using the Rust programming language afterwards.
Finally, the execution of the implemented client-server application was as expected.
This post contains the complete source code of the implemented example. When you look at it, you will see that there was no need for manually network programming.
The FEPCOS-J and FEPCOS-R development tools automated the network programming required to implement a Java server that is accessed by a Rust client.
References
- G. Fuchs: FEPCOS-J (4) Easy programming of a multithreaded TCP/IP server in Java; At: Foojay Today; 2024-03-21; https://foojay.io/today/fuchs-2024-fepcos-j-multithreaded-server/.
- G. Fuchs: Video: Easy Implementation of a Client-Server Application in Java with FEPCOS-J; On: YouTube, FEPCOS-Project (@FepcosInfo); 2024-07-29; https://youtu.be/qtPP7kZbriQ.
- Fepcos-Project: FEPCOS-J; http://fepcos.info/en/fepcos-j.html.
- G. Fuchs: "FEPCOS-J (1) – Description, Impressions of Usage, Current State"; https://foojay.io/today/fuchs-2023-fepcos-j-01/.
- G. Fuchs: "FEPCOS-J (2) – Declaratively compose networked systems in Java"; https://foojay.io/today/fuchs-2023-fepcos-j-02/.
- G. Fuchs: "FEPCOS-J (3) – Build native executables of Java-coded networked systems"; https://foojay.io/today/fuchs-2023-fepcos-j-03-native-executables/.
- Fepcos-Project: FEPCOS-Model; http://fepcos.info/en/fepcos-model.html.
- S. Truchat, G. Fuchs, S. Meyer, and F. Dressler: An Adaptive Model for Reconfigurable Autonomous Services using Profiling; In: International Journal of Pervasive Computing and Communications (JPCC), Special Issue on Pervasive Management, 2.3 (2006), pp. 247-260; doi: 10.1108/17427370780000154.
- G. Fuchs: Profiling von Mobilen Autonomen Diensten - Theorie und Konzepte am Beispiel eines Prototyps; VDM Verlag Dr. Müller, Saarbrücken, DE-SL; 2008; ISBN:978-3-639-06501-5 (in German).
- G. Fuchs, S. Truchat, and F. Dressler: Distributed Software Management in Sensor Networks using Profiling Techniques; In: Proceedings of the 1st IEEE/ACM International Conference on Communication System Software and Middleware (COMSWARE): 1st International Workshop on Software for Sensor Networks (SensorWare); New Delhi, IN-DL; IEEE, 2006; doi: 10.1109/COMSWA.2006.1665225.
- Rust Team: Rust; https://www.rust-lang.org/.
All references were last accessed on October 1, 2024.