LoginSignup
0
0

More than 1 year has passed since last update.

Micronaut を使って ATP と連携する Function を簡単に実装する

Last updated at Posted at 2022-12-05

はじめに

この記事は、 JPOUG Advent Calendar 2022 5 日目の記事です。4 日目は wmo6hash さんの「働き方の新しいスタイル」への仕事道具最適化'22 でした!

私の記事では、以前書いた Micronaut on Oracle Functions ことはじめ の続編ということで、ATP(Autonomous Transaction Processing)と連携する Function を実装してみたいと思います。

全体像

以下のような構成を作成します。

image01.png

手順

作成済みのサンプルコードはこちらに格納してあります。詳細な箇所は格納済みのサンプルコードをご参照ください。

前提

  • OCI のアカウントを有していること
  • ATP が作成済みであること
  • OCI Vault が使えること
  • 適切なポリシー設定がされている OCI の VM

ひな形の作成

例によって、https://micronaut.io/launch/ を使いますが、手元に Micronaut CLI がある方はそっちを使ってもいいです。

curl \
    --location \
    --request GET 'https://launch.micronaut.io/create/default/me.shukawam.country?lang=JAVA&build=GRADLE&test=JUNIT&javaVersion=JDK_11&features=oracle-cloud-atp&features=oracle-cloud-vault&features=oracle-function&features=data-jpa' \
    --output country.zip

含めている features を簡単に補足しておきます。

  • oracle-cloud-atp
    • ATP と接続するための Wallet のダウンロードや展開等を実施してくれる機能です
  • oracle-cloud-vault
    • 機微情報(データベースの接続に用いるユーザー名、パスワード、等)を OCI Vault から参照するための機能です
  • oracle-function
    • Oracle Functions として Micronaut アプリケーションを実行するための機能です
  • data-jpa
    • データベースアクセスに用います
    • Spring Data を使ったことある方でしたらかなり取っつきやすいはず

解凍します。

unzip country.zip

含まれているファイル群を確認します。(※説明のために一部コメントを追記しています)

country
├── README.md
├── build.gradle
├── func.yml
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── micronaut-cli.yml
├── settings.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── me
    │   │       └── shukawam
    │   │           ├── Application.java
    │   │           └── CountryController.java
    │   └── resources
    │       ├── application.yml # アプリケーションの設定
    │       ├── bootstrap.yml # application.yml のコンテキストを作成するために必要な設定を記述する(e.g. OCI への認証情報、OCI Vault, etc.)
    │       └── simplelogger.properties
    └── test
        ├── java
        │   └── me
        │       └── shukawam
        │           ├── CountryControllerTest.java
        │           └── CountryTest.java
        └── resources
            └── application-test.yml

ATP と接続するための設定

ATP と接続するための設定を追記します。

  • bootstrap.yml
    • application.yml のコンテキストを生成するために必要な設定周りを記述します
    • e.g. OCI の認証情報,OCI Vault, etc.
  • application.yml
    • アプリケーションの設定を記述します
    • e.g. ATP の接続情報, etc.

bootstrap.yml

生成されている bootstrap.yml を以下の内容で更新します。(.oci.vault.vaults.*のインデントが生成時にずれているので注意してください)

micronaut:
  application:
    name: micronautCountry
  config-client:
    enabled: true
  server:
    context-path: /sandbox # API Gateway - Deployment の Prefix Path を設定する
oci:
  vault:
    config:
      enabled: true
    vaults:
      - compartment-ocid: "<your-compartment-id>"
        ocid: "<your-vault-ocid>"

OCI Vault の Secret に必要な情報を格納する

Micronaut アプリケーションから参照するための情報を格納します。今回は、以下の情報を格納しています。

  • countryDS.ocid
    • ATP の OCID
  • countryDS.walletPassword
    • Wallet のダウンロード、展開に必要なパスワード
  • countryDS.user
    • ATP に接続するユーザー
  • countryDS.password
    • ATP に接続するユーザーのパスワード

application.yml

生成されている application.yml を以下の内容で更新します。
${<secret-name>:<default-value>}と記述すると、起動時に Secret の値に置換されます。以下、例で <default-value> を空にしているのは必ず Vault から参照させるためです。

datasources:
  default:
    ocid: ${countryDS.ocid:}
    walletPassword: ${countryDS.walletPassword:}
    dialect: ORACLE
    username: ${countryDS.user:}
    password: ${countryDS.password:}
oci.config.profile: DEFAULT
jpa.default.properties.hibernate.hbm2ddl.auto: update

Country.java

Entity クラスを定義します。

Country.java
package me.shukawam.country;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity(name = "Country")
@Table(name = "COUNTRY")
public class Country {
    @Id
    @Column(name = "COUNTRY_ID")
    private String countryId;
    @Column(name = "COUNTRY_NAME")
    private String countryName;

    /**
     * @return the countryId
     */
    public String getCountryId() {
        return countryId;
    }

    /**
     * @param countryId the countryId to set
     */
    public void setCountryId(String countryId) {
        this.countryId = countryId;
    }

    /**
     * @return the countryName
     */
    public String getCountryName() {
        return countryName;
    }

    /**
     * @param countryName the countryName to set
     */
    public void setCountryName(String countryName) {
        this.countryName = countryName;
    }

}

CountryRepository.java

DB アクセスを行う RepositoryInterface を CrudRepository を継承して作成します。CrudRepository には、基本的な CRUD 処理を行うメソッドが生えています。

CountryRepository.java
package me.shukawam.country;

import io.micronaut.data.annotation.Repository;
import io.micronaut.data.repository.CrudRepository;

@Repository
public interface CountryRepository extends CrudRepository<Country, String> {

}

CountryService.java

今回程度の例だと冗長ですが、一応 Service も実装します。

CountryService.java
package me.shukawam.country;

import java.util.ArrayList;
import java.util.List;

import javax.ws.rs.NotFoundException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jakarta.inject.Inject;
import jakarta.inject.Singleton;

@Singleton
public class CountryService {
    private final CountryRepository countryRepository;
    private static final Logger logger = LoggerFactory.getLogger(CountryService.class);

    /**
     * @param countryRepository
     */
    @Inject
    public CountryService(CountryRepository countryRepository) {
        this.countryRepository = countryRepository;
    }

    public List<Country> getAllCountry() {
        logger.debug("Inside CountryService#getAllCountry");
        List<Country> countryList = new ArrayList<>();
        countryRepository.findAll().iterator().forEachRemaining(countryList::add);
        return countryList;
    }

    public Country getCountryById(String countryId) {
        logger.debug("Inside CountryService#getCountryById");
        var optionalCountry = countryRepository.findById(countryId);
        if (optionalCountry.isEmpty()) {
            logger.info("country: %s is not found.", countryId);
            throw new NotFoundException(String.format("country: %s is not found.", countryId));
        } else {
            return optionalCountry.get();
        }
    }

}

CountryController.java

REST API のエントリーポイントとなるクラスを作成します。

CountryController.java
package me.shukawam.country;

import java.util.List;

import javax.ws.rs.core.MediaType;

import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Produces;
import jakarta.inject.Inject;

@Controller("/country")
public class CountryController {
    private final CountryService countryService;

    /**
     * @param countryService
     */
    @Inject
    public CountryController(CountryService countryService) {
        this.countryService = countryService;
    }

    @Get
    @Produces(MediaType.APPLICATION_JSON)
    public List<Country> getAllCountry() {
        return countryService.getAllCountry();
    }

    @Get("/id/{countryId}")
    public Country getCountryById(String countryId) {
        return countryService.getCountryById(countryId);
    }

}

手元で動作確認

手元で動作確認します。InstancePrincipal が適切に設定されている OCI の VM であれば以下コマンドで動作します。(-t で Hot Reload がサポートされます)

./gradlew run -t
# ... omit
[main] INFO io.micronaut.runtime.Micronaut - Startup completed in 13951ms. Server Running: http://localhost:8080

公開されている API を叩いてみます。

curl http://localhost:8080/sandbox/country/id/JP | jq
{
  "countryId": "JP",
  "countryName": "日本国"
}

OK!

Oracle Functions にデプロイ

Micronaut on Oracle Functions ことはじめ - Oracle Functions にデプロイする をご参照ください mm

API Gateway の設定

Micronaut on Oracle Functions ことはじめ - API Gateway で公開する をご参照ください mm

API Gateway + Oracle Functions の構成で動作確認

API Gateway + Oracle Functions でも同様のレスポンスが得られることが確認できます。

curl <api-gateway-host>/sandbox/country/id/JP | jq
{
  "countryId": "JP",
  "countryName": "日本国"
}

終わりに

いかがでしたでしょうか?Micronaut を使うことで Oracle Functions であっても簡単に(かつ開発体験を損ねることなく) ATP アクセスするアプリケーションを実装することができました。また、Micronaut は GraalVM による Native Image 生成と非常に相性が良いです。次回の記事では、今回作成したアプリケーションの Native Image 化にも取り組んでみたいと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0