1. 概要

Helidon は、最近Oracleによってオープンソース化された新しいJavaマイクロサービスフレームワークです。 これは、J4C(Java for Cloud)という名前でOracleプロジェクトの内部で使用されていました。

このチュートリアルでは、フレームワークの主要な概念について説明し、次にヘリドンベースのマイクロサービスの構築と実行に移ります。

2. プログラミングモデル

現在、フレームワークは、マイクロサービスを作成するための2つのプログラミングモデルをサポートしています。HelidonSEとHelidonMPです。

Helidon SEは、リアクティブプログラミングモデルをサポートするマイクロフレームワークとして設計されていますが、Helidon MPは、JakartaEEコミュニティがポータブルな方法でマイクロサービスを実行できるようにするEclipseMicroProfileランタイムです。

どちらの場合も、HelidonマイクロサービスはJava SEアプリケーションであり、mainメソッドから小さなHTTPサーバーを起動します。

3. ヘリドンSE

このセクションでは、Helidon SEの主なコンポーネントであるWebServer、Config、およびSecurityについて詳しく説明します。

3.1. Webサーバーのセットアップ

WebServer API を開始するには、必要なMaven依存関係pom.xmlファイルに追加する必要があります。

<dependency>
    <groupId>io.helidon.webserver</groupId>
    <artifactId>helidon-webserver</artifactId>
    <version>0.10.4</version>
</dependency>

単純なWebアプリケーションを作成するには、次のビルダーメソッドのいずれかを使用できます:WebServer.create(serverConfig、routing)またはWebServer.create(routing)。 最後の1つは、サーバーがランダムなポートで実行できるようにするデフォルトのサーバー構成を採用しています。

これは、事前定義されたポートで実行される単純なWebアプリケーションです。 また、「/greet」パスとGETメソッドを使用してHTTPリクエストのグリーティングメッセージで応答するシンプルなハンドラーも登録しました。

public static void main(String... args) throws Exception {
    ServerConfiguration serverConfig = ServerConfiguration.builder()
      .port(9001).build();
    Routing routing = Routing.builder()
      .get("/greet", (request, response) -> response.send("Hello World !")).build();
    WebServer.create(serverConfig, routing)
      .start()
      .thenAccept(ws ->
          System.out.println("Server started at: http://localhost:" + ws.port())
      );
}

最後の行は、サーバーを起動し、HTTPリクエストの処理を待つことです。 ただし、このサンプルコードをmainメソッドで実行すると、次のエラーが発生します。

Exception in thread "main" java.lang.IllegalStateException: 
  No implementation found for SPI: io.helidon.webserver.spi.WebServerFactory

WebServer は実際にはSPIであり、ランタイム実装を提供する必要があります。 現在、 Helidonは、Nettyコアに基づくNettyWebServer実装を提供しています。

この実装のMaven依存関係は次のとおりです。

<dependency>
    <groupId>io.helidon.webserver</groupId>
    <artifactId>helidon-webserver-netty</artifactId>
    <version>0.10.4</version>
    <scope>runtime</scope>
</dependency>

これで、メインアプリケーションを実行し、構成されたエンドポイントを呼び出して、それが機能することを確認できます。

http://localhost:9001/greet

この例では、ビルダーパターンを使用してポートとパスの両方を構成しました。

Helidon SEでは、構成データが ConfigAPIによって提供される構成パターンを使用することもできます。 これは次のセクションの主題です。

3.2. Config API

Config APIは、構成ソースから構成データを読み取るためのツールを提供します

Helidon SEは、多くの構成ソースの実装を提供します。 デフォルトの実装は、 helidon-config によって提供されます。ここで、構成ソースは、クラスパスの下にあるapplication.propertiesファイルです。

<dependency>
    <groupId>io.helidon.config</groupId>
    <artifactId>helidon-config</artifactId>
    <version>0.10.4</version>
</dependency>

構成データを読み取るには、デフォルトのビルダーを使用する必要があります。このビルダーは、デフォルトで application.properties:から構成データを取得します。

Config config = Config.builder().build();

application.propertiesファイルをsrc/ main /resourceディレクトリの下に次の内容で作成しましょう。

server.port=9080
web.debug=true
web.page-size=15
user.home=C:/Users/app

値を読み取るには、Config.get()メソッドを使用してから、対応するJavaタイプに簡単にキャストできます。

int port = config.get("server.port").asInt();
int pageSize = config.get("web.page-size").asInt();
boolean debug = config.get("web.debug").asBoolean();
String userHome = config.get("user.home").asString();

実際、デフォルトのビルダーは、最初に見つかったファイルを次の優先順位でロードします: application.yaml、application.conf、application.json、およびapplication.properties。最後の3つの形式には、追加の関連する構成依存関係が必要です。 たとえば、YAML形式を使用するには、関連するYAML config依存関係を追加する必要があります。

<dependency>
    <groupId>io.helidon.config</groupId>
    <artifactId>helidon-config-yaml</artifactId>
    <version>0.10.4</version>
</dependency>

次に、application.ymlを追加します。

server:
  port: 9080  
web:
  debug: true
  page-size: 15
user:
  home: C:/Users/app

同様に、JSON簡略化形式またはJSON形式であるCONFを使用するには、helidon-config-hocon依存関係を追加する必要があります。

これらのファイルの構成データは、環境変数およびJavaシステムプロパティによって上書きされる可能性があることに注意してください。

環境変数とシステムプロパティを無効にするか、構成ソースを明示的に指定することで、デフォルトのビルダーの動作を制御することもできます

ConfigSource configSource = ConfigSources.classpath("application.yaml").build();
Config config = Config.builder()
  .disableSystemPropertiesSource()
  .disableEnvironmentVariablesSource()
  .sources(configSource)
  .build();

クラスパスから構成データを読み取ることに加えて、2つの外部ソース構成、つまりgitとetcdの構成を使用することもできます。 このためには、helidon-config-githelidon-git-etcdの依存関係が必要です。

最後に、これらの構成ソースのすべてが私たちのニーズを満たさない場合、Helidonは私たちが構成ソースの実装を提供することを許可します。 たとえば、データベースから構成データを読み取ることができる実装を提供できます。

3.3. ルーティングAPI

Routing APIは、HTTPリクエストをJavaメソッドにバインドするメカニズムを提供します。これは、リクエストメソッドとパスを一致基準として使用するか、RequestPredicateオブジェクトを使用してより多くの基準を使用することで実現できます。 。

したがって、ルートを構成するには、HTTPメソッドを基準として使用できます。

Routing routing = Routing.builder()
  .get((request, response) -> {} );

または、HTTPメソッドをリクエストパスと組み合わせることができます。

Routing routing = Routing.builder()
  .get("/path", (request, response) -> {} );

RequestPredicateを使用してさらに制御することもできます。 たとえば、既存のヘッダーまたはコンテンツタイプを確認できます。

Routing routing = Routing.builder()
  .post("/save",
    RequestPredicate.whenRequest()
      .containsHeader("header1")
      .containsCookie("cookie1")
      .accepts(MediaType.APPLICATION_JSON)
      .containsQueryParameter("param1")
      .hasContentType("application/json")
      .thenApply((request, response) -> { })
      .otherwise((request, response) -> { }))
      .build();

これまで、機能的なスタイルのハンドラーを提供してきました。 Service クラスを使用して、より高度な方法でハンドラーを作成することもできます。

それでは、最初に、作業しているオブジェクトのモデル、Bookクラスを作成しましょう。

public class Book {
    private String id;
    private String name;
    private String author;
    private Integer pages;
    // ...
}

Service.update()メソッドを実装することで、BookクラスのRESTサービスを作成できます。 これにより、同じリソースのサブパスを構成できます。

public class BookResource implements Service {

    private BookManager bookManager = new BookManager();

    @Override
    public void update(Routing.Rules rules) {
        rules
          .get("/", this::books)
          .get("/{id}", this::bookById);
    }

    private void bookById(ServerRequest serverRequest, ServerResponse serverResponse) {
        String id = serverRequest.path().param("id");
        Book book = bookManager.get(id);
        JsonObject jsonObject = from(book);
        serverResponse.send(jsonObject);
    }

    private void books(ServerRequest serverRequest, ServerResponse serverResponse) {
        List<Book> books = bookManager.getAll();
        JsonArray jsonArray = from(books);
        serverResponse.send(jsonArray);
    }
    //...
}

また、メディアタイプをJSONとして構成したため、この目的のためにhelidon-webserver-json依存関係が必要です。

<dependency>
    <groupId>io.helidon.webserver</groupId>
    <artifactId>helidon-webserver-json</artifactId>
    <version>0.10.4</version>
</dependency>

最後に、ルーティングビルダーのregister()メソッドを使用して、ルートパスをリソースにバインドします。この場合、サービスによって構成されたパスのプレフィックスはルートパスです。

Routing routing = Routing.builder()
  .register(JsonSupport.get())
  .register("/books", new BookResource())
  .build();

これで、サーバーを起動してエンドポイントを確認できます。

http://localhost:9080/books
http://localhost:9080/books/0001-201810

3.4. 安全

このセクションでは、セキュリティモジュールを使用してリソースを保護します。

必要なすべての依存関係を宣言することから始めましょう。

<dependency>
    <groupId>io.helidon.security</groupId>
    <artifactId>helidon-security</artifactId>
    <version>0.10.4</version>
</dependency>
<dependency>
    <groupId>io.helidon.security</groupId>
    <artifactId>helidon-security-provider-http-auth</artifactId>
    <version>0.10.4</version>
</dependency>
<dependency>
    <groupId>io.helidon.security</groupId>
    <artifactId>helidon-security-integration-webserver</artifactId>
    <version>0.10.4</version>
</dependency>

helidon-security helidon-security-provider-http-auth 、および helidon-security-integration-webserver の依存関係は、MavenCentralから入手できます。

セキュリティモジュールは、認証と承認のための多くのプロバイダーを提供します。 この例では、HTTP基本認証プロバイダーを使用します。これは非常に単純ですが、他のプロバイダーのプロセスはほとんど同じです。

最初に行うことは、セキュリティインスタンスを作成することです。 簡単にするためにプログラムで行うこともできます

Map<String, MyUser> users = //...
UserStore store = user -> Optional.ofNullable(users.get(user));

HttpBasicAuthProvider httpBasicAuthProvider = HttpBasicAuthProvider.builder()
  .realm("myRealm")
  .subjectType(SubjectType.USER)
  .userStore(store)
  .build();

Security security = Security.builder()
  .addAuthenticationProvider(httpBasicAuthProvider)
  .build();

または、構成アプローチを使用できます。

この場合、 ConfigAPIを介してロードするapplication.ymlファイルですべてのセキュリティ構成を宣言します。

#Config 4 Security ==> Mapped to Security Object
security:
  providers:
  - http-basic-auth:
      realm: "helidon"
      principal-type: USER # Can be USER or SERVICE, default is USER
      users:
      - login: "user"
        password: "user"
        roles: ["ROLE_USER"]
      - login: "admin"
        password: "admin"
        roles: ["ROLE_USER", "ROLE_ADMIN"]

  #Config 4 Security Web Server Integration ==> Mapped to WebSecurity Object
  web-server:
    securityDefaults:
      authenticate: true
    paths:
    - path: "/user"
      methods: ["get"]
      roles-allowed: ["ROLE_USER", "ROLE_ADMIN"]
    - path: "/admin"
      methods: ["get"]
      roles-allowed: ["ROLE_ADMIN"]

それをロードするには、 Config オブジェクトを作成してから、 Security.fromConfig()メソッドを呼び出す必要があります。

Config config = Config.create();
Security security = Security.fromConfig(config);

Security インスタンスを取得したら、最初に WebSecurity.from()メソッドを使用してWebServerに登録する必要があります。

Routing routing = Routing.builder()
  .register(WebSecurity.from(security).securityDefaults(WebSecurity.authenticate()))
  .build();

WebSecurity インスタンスを、セキュリティとWebサーバー構成の両方をロードする構成アプローチを使用して直接作成することもできます。

Routing routing = Routing.builder()        
  .register(WebSecurity.from(config))
  .build();

これで、 /userおよび/admin パスのハンドラーを追加し、サーバーを起動して、それらにアクセスしてみることができます。

Routing routing = Routing.builder()
  .register(WebSecurity.from(config))
  .get("/user", (request, response) -> response.send("Hello, I'm Helidon SE"))
  .get("/admin", (request, response) -> response.send("Hello, I'm Helidon SE"))
  .build();

4. ヘリドンMP

HelidonMPはEclipseMicroProfileの実装であり、MicroProfileベースのマイクロサービスを実行するためのランタイムも提供します。

すでにEclipseMicroProfile に関する記事があるので、そのソースコードをチェックして、HelidonMPで実行するように変更します。

コードをチェックアウトした後、すべての依存関係とプラグインを削除し、HelidonMPの依存関係をPOMファイルに追加します。

<dependency>
    <groupId>io.helidon.microprofile.bundles</groupId>
    <artifactId>helidon-microprofile-1.2</artifactId>
    <version>0.10.4</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-binding</artifactId>
    <version>2.26</version>
</dependency>

helidon-microprofile-1.2およびjersey-media-json-bindingの依存関係はMavenCentralから入手できます。

次に、 src / main / resource / META-INFディレクトリの下に、次の内容のbeans.xmlファイルを追加します。

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
  http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
  version="2.0" bean-discovery-mode="annotated">
</beans>

LibraryApplication クラスで、 getClasses()メソッドをオーバーライドして、サーバーがリソースをスキャンしないようにします。

@Override
public Set<Class<?>> getClasses() {
    return CollectionsHelper.setOf(BookEndpoint.class);
}

最後に、mainメソッドを作成し、次のコードスニペットを追加します。

public static void main(String... args) {
    Server server = Server.builder()
      .addApplication(LibraryApplication.class)
      .port(9080)
      .build();
    server.start();
}

以上です。 これで、すべての本のリソースを呼び出すことができるようになります。

5. 結論

この記事では、ヘリドンの主要コンポーネントについて説明し、ヘリドンSEとMPのいずれかをセットアップする方法も示しました。 HelidonMPは単なるEclipseMicroProfileランタイムであるため、これを使用して既存のMicroProfileベースのマイクロサービスを実行できます。

いつものように、上記のすべての例のコードは、GitHubにあります。