Ballerina は、 Cloud Native Programming Language を謳う
プログラミング言語です。
Ballerina は、現在の micro service, network end point と
いうソフトウェア開発を
強く意識したプログラミング言語です。 思想として、クラウド上での
micro service を実現できるように、サーキットブレーカーや
ロードバランシング、エラーハンドリングなど、複数のプロセスが連動する
ような場合に必須となる機能を実装を言語層で容易におこなえることを
特徴としています。
Balerina は、 基本的に micro service として単一のサービスを
実装した エンドポイントを 組合せてソフトウェアを構築することを
想定しています。 このため、 Hello World の例も、 "Hello World" を
出力するサービスとそれを受け取るクライアントで表現されることが
多いようです。
import ballerina/http;
service hello on new http:Listener(9090) {
resource function sayHello(http:Caller caller, http:Request req) returns error? {
check caller->respond("Hello World");
}
}
service は、 そのプログラムで実装するサービスを定義するシンタックスに
なります。
上記の例では、 hello というサービスを httpプロトコルで、Port: 9090 にて
提供することを宣言しています。
resource は、 そのサービスが提供する機能の定義をしています。
caller は、このサービスを呼び出したクライアントを意味するオブジェクトと
なっており、上記のプログラムでは、caller に対して "Hello World" を
返却するというロジックになっています。
今回の場合、 サービスは http として実装しており、 caller も http の
Caller として定義しているので、 返却先は 呼び出した http client で、
返却データは http の Response Bodyとして通知されます。
import ballerina/http;
import ballerina/io;
public function main() returns @tainted error? {
http:Client clientEp = new ("http://localhost:9090");
http:Response resp = check clientEp->get("/hello/sayHello/");
string payload = check resp.getTextPayload();
io:println(payload);
}
clientEp は "http://localhost:9090" に対し要求を行う http client として 定義しています。 上記のプログラムでは、 clientEp に対して、 "GET /hello/sayHello/" という RestAPI を実行して、その通信結果を resp に受けています。 RestAPI の処理結果は http Response Body に含まれるので、 resp.getTextPayload() にて取得して、標準出力へ出力しています。
Ballerina 特有の考え方に、外部から受けとるデータは汚染されているという
考え方があります。
これは、 Ballerina で実装するプログラムは基本的に network end point と
なることを想定しているため、SQLインジェクションなど、悪意のあるアクセスは
ありえるものと想定しているためです。
このため、 Ballerina では、 関数を定義するときに汚染されていない
(検証済みの)データしか受け取れないように宣言
(引数宣言時に @untinted を付与する)し、呼び出し側では、汚染されて
いなことを検証したこと示す (<@untinted>でキャストする)ことで、
データの安全性を担保するしくみを持っています。
以下に、例題プログラムを示します。
import ballerina/http;
import ballerina/io;
import ballerina/lang.'float;
public function main(string... args) returns @tainted error? {
http:Client sunriseApi = new("http://api.sunrise-sunset.org");
if (args.length() != 2 ) {
io:println("Invalid argument.");
}
float|error lat = 'float:fromString(args[0]);
float|error lng = 'float:fromString(args[1]);
if (lat is float && lng is float) {
http:Response sunriseResp = check sunriseApi-> get("/json?lat=" + <@untinted> lat.toString() + "&lng=" + <@untainted>lng.toString());
json sunrisePayload = check sunriseResp.getJsonPayload();
io:println(sunrisePayload);
} else {
io:println("Invalid argument.");
}
}
上記のプログラムでは、引数で受けとった浮動小数をそれぞれ緯度/経度として、
その土地の日の出、日の入りなど情報を出力する簡単なプログラムです。
get()メソッドの第一引数は、汚染されていないデータしか受けとれません。
このため、lat と lng に引数の値を格納したときに、浮動小数であることを
検証し、その検証済の証しとして <@untainted> にてキャストをするという
対応をしています。