ノート:

OKEでのSpring Bootを使用したOracle GraalVMベースのJavaアプリケーションの作成によるSOAPメッセージのATPへの格納およびOCIキューへの送信

イントロダクション

お客様の多くは、アプリケーション間の通信を可能にするために、従来のSimple Object Access Protocol(SOAP)メッセージングを利用しています。多くの場合、トランザクション・データの格納、サービスの分離の確保、受信リクエストのロード・バランシングの実現、およびこれらのメッセージの非同期通信の有効化が必要です。

このチュートリアルでは、Oracle Cloud Infrastructure Kubernetes Engine (OKE)インフラストラクチャにデプロイされたSpring Bootを使用して、Oracle GraalVMベースのJavaアプリケーションを構築する方法を説明します。このアプリケーションは、Oracle Autonomous Transaction Processing (ATP)やOCI Queueなどの様々なOracle Cloud Infrastructure (OCI)サービスを持つトランザクション・インテグレータとして使用されます。この設定により、システムは直接接続なしで対話できるため、処理速度が異なるアプリケーション間の通信が容易になります。また、情報がデータベースに格納されると、参照または分析できます。

以下の技術を使用します。

Oracle Cloud Infrastructure Services(OCI): OCIは、150以上のクラウド・サービスを提供する、安全で高パフォーマンスのクラウド・プラットフォームです。スケーラビリティ、セキュリティおよびパフォーマンスのために設計されています。

Oracleテクノロジ:

その他のテクノロジ:

OCIハイ・レベル・アーキテクチャ:

OCIアーキテクチャ

ユースケース・アーキテクチャ

ノート:

目的

前提条件

タスク1: OKEクラスタのプロビジョニングおよび構成

このタスクでは、Kubernetesプラットフォームをプロビジョニングします。このプラットフォームでは、ATPに格納するすべてのSOAP高トランザクション・メッセージをサポートし、それぞれをリアルタイムでOCIキューに送信します。

  1. OCIコンソールにログインし、「開発者サービス」「Kubernetesクラスタ(OKE)」に移動して、プリファレンスの「コンパートメント」を選択します。

    OKEクラスタを作成する方法は2つあります。

    • クイック作成
    • Custom Create.
  2. 「クイック作成」を選択します。この方法は、OKEが操作に必要なすべての要素を次のように簡単かつ高速に、自動的にデプロイするためです。

    • 仮想クラウド・ネットワーク(VCN)
    • インターネット・ゲートウェイ
    • NAT (Network Address Translation)ゲートウェイ。
    • サービス・ゲートウェイ
    • Kubernetesクラスタ
    • Kubernetesワーカー・ノードおよびノード・プール

    ノート:すでに顧客がサービス、ネットワーク、インフラストラクチャを所有しているエンタープライズ環境では、OKEデプロイメントをコンプライアンスに準拠するようにカスタマイズし、クライアントのアーキテクチャ、リソースおよびベスト・プラクティスに従ってカスタマイズすることが重要です。

    クイック作成

  3. 「クラスタの作成」をクリックし、次の情報を入力します。

    • 名前: OKEクラスタの名前を入力します。
    • コンパートメント:このプロジェクト用に作成されたコンパートメントを選択します。
    • Kubernetesバージョン:使用可能な最新のKubernetesバージョンを選択します。
    • Kubernetes APIエンドポイント:このチュートリアルでは、「パブリック・エンドポイント」を選択しますが、「プライベート・エンドポイント」を選択することもできます。
    • ノード・タイプ: 「管理対象」ノードを選択します。
    • Kubernetesワーカー・ノード: 「プライベート・ワーカー」を選択します。
    • シェイプおよびイメージ: VM.Standard.E5を選択します。フレックスでは、OCPUの数(2)およびメモリー(16GB)をカスタマイズし、デフォルトのOracle Linux 8イメージを保存します。
    • ノード数: OKEノード・プールとともにデプロイするワーカー・ノードを2つ入力します。

    OKEクラスタの作成

    OKEクラスタが動作していることを確認します。

    OKE実行中

タスク2: OCI Container Registry Classicのプロビジョニング、構成およびアクセス

リポジトリ内のプロジェクト・イメージを管理する必要があります。これを実現するために、OCI Container Registry Classicをプロビジョニングします。イメージがOCI Container Registry Classicに格納されると、OKEにデプロイできるようになります。

  1. OCIコンソールに移動し、「開発者サービス」「コンテナおよびアーティファクト」「コンテナ・レジストリ」に移動して、「リポジトリの作成」をクリックします。

  2. 次の情報を入力し、「Create」をクリックします。

    • コンパートメントに作成:このプロジェクト用に作成されたコンパートメントを選択します。
    • アクセス: 「パブリック」を選択します。
    • リポジトリ名: springboot/tutorialappと入力します。

    リポジトリの作成

  3. リポジトリが作成されたら、Oracle管理ホストから次のコマンドを使用してアクセスします。

    docker login -u 'tenancy_namespace/domain/username' regionID.ocir.io
    
    Password: xxxxxx
    

    Oracleレジストリ認証

    OK

タスク3: Oracle Autonomous Transaction Processing (ATP)サーバーレス・データベースのプロビジョニングおよび構成

ATPデータベースでは、トランザクションごとに受信した各SOAPメッセージのデータを格納します。およそ各メッセージの順序はミリ秒で、パラレル挿入と順次挿入が行われます。

  1. OCIコンソールに移動し、「Oracle Database」に移動して「Autonomous Transaction Processing」をクリックします。

  2. 「Autonomous Databaseの作成」をクリックし、次の情報を入力します。

    • コンパートメントの選択:このプロジェクト用に作成されたコンパートメントを選択します。
    • 表示名: 表示名を入力します。
    • データベース名の表示:データベース名を入力します。
    • ワークロード・タイプの選択: 「トランザクション処理」を選択します。
    • デプロイメント・タイプの選択: 「サーバーレス」を選択します。
    • データベースを構成します:
      • 開発者:選択を解除します。
      • データベース・バージョンの選択: 「23ai」を選択します。
      • ECPU数: 2と入力します。
      • コンピュート自動スケーリング:選択してください。
      • ストレージ: 1024GBと入力します。
    • 自動バックアップ保持期間(日数):デフォルト・オプションは60日のままにします。
    • 管理者資格証明を作成します:
      • ユーザー名:デフォルトではADMINであり、編集できません。
      • パスワード:優先パスワードを入力します。
      • パスワードの確認: パスワードを再度入力します。
    • ネットワーク・アクセスの選択: 「プライベート・エンドポイント・アクセスのみ」を選択し、このプロジェクト用に作成されたVCNおよびサブネットを選択します。この設定により、指定したプライベート・ネットワーク(VCN)への接続のみが制限されます。ただし、他のオプションを選択することもできますが、これは会社のニーズによって異なります。

    Oracle ATPデータベースの作成

    Oracle ATPデータベースの作成

    Oracle ATPデータベースの作成

    ATPデータベースが実行中であることを確認します。

    ATP実行中

タスク4: Oracle Autonomous Transaction Processing (ATP)でのプロジェクト表の接続および作成

ここでは、タスク3で生成されたATPデータベースにプロジェクト表を構成、接続および作成する必要があります。

  1. OCIコンソールに移動し、「Oracle Database」「Autonomous Transaction Processing」に移動し、「データベース接続」をクリックします。「TLS認証」「TLS」を選択し、「Walletのダウンロード」をクリックします。

    データベース接続

  2. ウォレットの.zipファイルを解凍し、tnsnames.oraでデータソースURLを取得して、このデータベースへの接続を取得できます。このデータソースURLを保存します。

    たとえば次のようにします。

    tutorialoracleatp_medium = (description= (retry_count=20)(retry_delay=3)(address=(protocol=tcps)(port=1522)(host=xxxxxxxx.adb.sa-saopaulo-1.oraclecloud.com))(connect_data=(service_name=xxxxxxxxxxxxxxx_tutorialoracleatp_medium.adb.oraclecloud.com))(security=(ssl_server_dn_match=no)))
    
  3. データベースへのアクセスが必要になりました。ATPがプロビジョニングされると、Oracle REST Data Services (ORDS)アクセスが有効になりました。詳細は、Oracle REST Data Servicesを参照してください。

    Autonomous Databaseの詳細」ページに移動し、「データベース・アクション」をクリックします。アクセスできるのは、同じ仮想クラウド・ネットワーク(VCN)で実行されているコンピュート・インスタンスからのみです。

    データベース・アクション

  4. ブラウザにURLを貼り付け、以前にATPデータベースに入力したユーザーおよびパスワードを使用してORDSにアクセスし、Oracle SQL Developer Webモジュールにアクセスします。

    ORDS

  5. 次の問合せを使用して、受信するSOAPメッセージに関連する表USERSCARSおよびHOUSESを作成します。

    CREATE TABLE USERS (
    username varchar(50) NOT NULL,
    userlastname varchar(50) NOT NULL,
    id int NOT NULL,
    email varchar(50) NOT NULL,
    dateuser varchar(50) NOT NULL,
    attributeuser varchar(50) NOT NULL  
    );
    
    CREATE TABLE CARS (
    userid int NOT NULL,
    brand varchar(50) NOT NULL,
    color varchar(50) NOT NULL,
    plate varchar(50) NOT NULL  
    );
    
    CREATE TABLE HOUSES (
    userid int NOT NULL,
    floors int NOT NULL,
    locationhouse varchar(50) NOT NULL,
    rooms int NOT NULL,
    bathrooms int NOT NULL
    );
    

    表がデータベース内に作成されました

タスク5: OCIキューのプロビジョニングおよび構成

OCI Queueでは、特定の期間、RESTful HTTP APIを介して高度にトランザクション・メッセージを格納します。コンシューマは、メッセージを即時に、または都合のよいときに読み取って削除し、データの損失を防ぎます。

  1. OCIコンソールに移動し、「開発者サービス」「アプリケーション統合」に移動し、「キュー」をクリックします。

  2. 「キューの作成」をクリックし、次の情報を入力して「キューの作成」をクリックします。

    • 名前:キューに適切な名前を入力します。
    • コンパートメント:作業コンパートメントを選択します。
    • キュー設定:このチュートリアルでは、「デフォルト構成」を選択しますが、必要に応じて、「表示タイムアウト」「最大保持期間」「最大チャネル消費」および「デッド・レター・キュー」設定などのビジネス・ニーズに応じて複数のオプションをカスタマイズできます。
    • 暗号化設定の構成: 「Oracle管理キー」を選択しますが、「顧客管理キー」もオプションです。

    キューの作成

    OCIキューが実行中であることを確認します。

    実行中のOCIキュー

タスク6: Spring Bootを使用したOracle GraalVMベースのJavaアプリケーションのビルドおよびOKEへのデプロイ

ここでは、次のタスクを実行するOracle GraalVMベースのJavaアプリケーションをSpring Bootで開発およびデプロイします:

ノート:開始する前に、「前提条件- 管理ホスト」および「前提条件- 開発環境」の項に示すように、管理ホストおよび開発環境を作成することが重要です。

管理ホストおよび開発環境が構成され、準備が整ったら、Spring Bootプロジェクトの開発を開始できます。

  1. Spring initializrに移動して、Spring Bootプロジェクトのフォルダ構造およびベース・ファイルを提供する最初のプロジェクトを作成し、後で要件に従って変更します。次の情報を入力して「生成」をクリックすると、Spring Bootプロジェクトが自動的にダウンロードされ、開発ホストに保存および解凍されます。

    • プロジェクト: 「Maven」を選択します。
    • 言語: 「Java」を選択します。
    • Spring boot: 3.3.6を選択します。
    • プロジェクト・メタデータ:
      • グループ: com.tutorial_springbootと入力します。
      • アーティファクト: tutorialと入力します。
      • 名前: tutorialと入力します。
      • 説明: Spring Boot Application (読取りSOAP、JSONへの変換、ATPの挿入およびOCIキューへの配置)と入力します。
    • パッケージ化: 「Jar」を選択します。
    • Java: 「17」を選択します。
    • 依存性: 「Oracleドライバ」「Spring Webサービス」および「Spring Web」を選択します。

    ノート:プロジェクトにいくつかの依存関係を追加し、後で必要に応じてpom.xmlファイルに直接追加できます。

    Springbootプロジェクト

  2. 春のブーツ構造プロジェクトです。

    構造Springブートプロジェクト

    pom.xmlファイルを確認して、作業を開始します。

    元のPOMファイル

    このチュートリアルで提案されているスコープに従って、pom.xmlファイルを更新します。

    • oci sdkバージョンおよび次の依存関係を追加します。

      • oci-java-sdk-common.
      • oci-java-sdk-queue.
      • oci-java-sdk-addons-oke-workload-identity.
      • oci-java-sdk-common-httpclient-jersey3.

      アプリケーションは、OCIで認証し、OCI QueueなどのOCIサービスを接続および管理する必要があります。

    • すでにpom.xmlファイルにはspring-boot-starter-web-services依存性があり、wsdl4j依存性を追加する必要があります。主な目的は、SOAPメッセージから受信したデータを取得し、Javaオブジェクトに格納し、XMLペイロードを操作するSpring Webサービスを作成し、契約優先SOAPサービス開発を促進することです。また、ポート、URIを構成し、XMLスキーマ定義(XSD)ファイルからロードされたXMLスキーマを設定することもできます。

    • JSON依存関係を追加します。このライブラリは、SOAPメッセージから抽出されたデータを使用してJSON形式を生成するために使用されます。

    • 「ビルド」セクションでspring-boot-maven-pluginプラグインを追加します。このプラグインにより、jar実行可能ファイルのSpring Bootプロジェクト・ファイルを生成できます。

    • 「ビルド」セクションでjaxb2-maven-pluginプラグインを追加します。このプラグインは、Java API for XMLバインディング(JAXB)を使用してXMLスキーマからJavaクラスを生成し、このようにして、SOAPメッセージから作成されたJavaクラス・オブジェクトにデータを渡すことができます。

      このプラグイン・セクションでは、パスを示す構成(Spring Bootプロジェクトに含まれるXSDファイル)を配置することが重要です。

      <configuration>
         <sources>
         <source>${project.basedir}/src/main/resources/messages.xsd<source>
         </sources>
      </configuration>
      
    • 「依存性」セクションにjasypt-spring-boot-starter依存性を追加し、「ビルド」セクションのjasypt-maven-pluginプラグインを追加して、application.propertiesファイル内の機密パラメータを暗号化し、アプリケーション内でのセキュアな使用を保証します。

    pom.xmlファイルに追加された次の依存関係を確認します。

    POMファイルが変更されました

    POMファイルが変更されました

  3. ライブラリをダウンロードし、次のコマンドを実行します。

    1. 開発環境で、次のコマンドを実行してプロジェクトにアクセスします。

      cd tutorial
      

      Springブートプロジェクトフォルダ

    2. プロジェクトを消去し、前のビルドで生成されたすべてのファイルを削除します。

      mvn clean
      

      Mavenクリーン

    3. ローカルmavenリポジトリからアーティファクトをパージ(削除およびオプションで再解決)します。

      mvn dependency:purge-local-repository
      

    Mavenパージ依存性

  4. すでに依存関係とpom.xmlファイルがプロジェクト内に構成されており、クライアント側からのリクエストを表すSOAP XMLファイルと、Spring Bootプロジェクト側でのリクエストを解釈するXSDファイルを確認します。

    1. このSOAP XMLファイルには2つのメッセージがあり、次の図に示すように、個人情報と、リクエストごとに送信する2つの異なるクライアントからのその他の種類の属性が含まれます。

      Soapファイル

    2. Spring Bootプロジェクト側では、Spring Webサービスによって自動的にWSDLとしてエクスポートされるWebサービス・ドメインを定義するためにXMLスキーマが必要です。次のイメージは、このチュートリアルに定義されているmessages.xsdファイルを示しています。

      messages.xsd:

      XSDファイル

    3. messages.xsdファイルをSpring Bootプロジェクトのresourcesフォルダに保存します。

      フォルダ内のXSDファイル

  5. jarファイルにプロジェクト・ファイルをビルドおよびインストールします。次のコマンドを実行して、Spring Bootプロジェクト・フォルダにいることを確認します。

    mvn install
    

    ノート: mavenインストール・コマンドが実行されると、ターゲット・フォルダが自動的に生成され、それと同じ方法で、以前に作成したXSDファイルに従ったJavaクラスと、プロジェクトの実行可能ファイル.jarファイルが生成されます。

    ターゲット・フォルダ

  6. これで、必要なJavaクラスをSpring Bootプロジェクトに追加できるようになりました。

    WebServiceConfig.java Class:このJavaクラスは、SOAP Webサービスを作成するために開発されました。

    • SOAPリクエストを処理するためのサーブレットを設定します。
    • XMLスキーマに基づいてWSDL定義を生成します。
    • SOAP Webサービスのアクセス・エンドポイントを定義します。
    • クラスパスからmessages.xsdスキーマ・ファイルを使用します。
    //Imports
    import org.springframework.boot.web.servlet.ServletRegistrationBean; //import the ServletRegistrationBean class
    import org.springframework.context.ApplicationContext; //import the ApplicationContext class
    import org.springframework.context.annotation.Bean; //import the Bean class
    import org.springframework.context.annotation.Configuration; //import the Configuration class
    import org.springframework.core.io.ClassPathResource; //import the ClassPathResource class
    import org.springframework.ws.config.annotation.EnableWs; //import the EnableWs class
    import org.springframework.ws.config.annotation.WsConfigurerAdapter; //import the WsConfigurerAdapter class
    import org.springframework.ws.transport.http.MessageDispatcherServlet; //import the MessageDispatcherServlet class
    import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition; //import the DefaultWsdl11Definition class
    import org.springframework.xml.xsd.SimpleXsdSchema; //import the SimpleXsdSchema class
    import org.springframework.xml.xsd.XsdSchema; //import the XsdSchema class
    
    //Configuration class for the Web Service configuration
    @EnableWs //Enable the Web Service
    @Configuration //Define the class as a Configuration class 
    
    public class WebServiceConfig extends WsConfigurerAdapter {
    
       //Create a ServletRegistrationBean object to register the MessageDispatcherServlet object with the application context
       @Bean
       public ServletRegistrationBean<MessageDispatcherServlet> messageDispatcherServlet(ApplicationContext applicationContext) {
          MessageDispatcherServlet servlet = new MessageDispatcherServlet(); //Create a MessageDispatcherServlet object
          servlet.setApplicationContext(applicationContext); //Set the application context for the MessageDispatcherServlet object
          servlet.setTransformWsdlLocations(true); //Set the transformWsdlLocations property to true
          return new ServletRegistrationBean<>(servlet, "/ws/*"); //Return a new ServletRegistrationBean object with the MessageDispatcherServlet object and the URL pattern
       }
    
       //Create a DefaultWsdl11Definition object to define the WSDL
       @Bean(name = "messages")
       public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema messagesSchema) {
          DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition(); //Create a DefaultWsdl11Definition object
          wsdl11Definition.setPortTypeName("MessagesPort"); //Set the port type name
          wsdl11Definition.setLocationUri("/ws"); //Set the location URI
          wsdl11Definition.setTargetNamespace("http://tutorial_example.com/ns0"); //Set the target namespace
          wsdl11Definition.setSchema(messagesSchema); //Set the schema
          return wsdl11Definition; //Return the DefaultWsdl11Definition object
       }
    
       //Create a XsdSchema object to define the schema
       @Bean
       public XsdSchema messagesSchema() {
          return new SimpleXsdSchema(new ClassPathResource("messages.xsd")); //Return a new SimpleXsdSchema object with the messages.xsd file
       }
    }
    

    ノート: Webサービスをテストする場合は、次のように、Spring Bootプロジェクトを同じローカル開発環境のデスクトップで実行し、curlを使用してHTTPリクエストを送信できます。

    mvn spring-boot:run
    

    Springブート・プロジェクトの実行

    Springブート・プロジェクトの実行

    プロジェクトが実行され、Webサービスが稼働したら、次のようにcurlを使用してローカルSOAP HTTPリクエストを実行します。

    curl --location 'http://localhost:8080/ws/'
    

    また、Spring Bootプロジェクトで公開されたWebサービスから応答が得られます。

    SOAP Webサービス

  7. modelというフォルダを作成し、このフォルダに次のJavaクラスを追加します。

    モデル・フォルダ

    ノート:これらのJavaクラスCarHouseおよびUserは、HTTP SOAPリクエストから抽出された各SOAPメッセージのデータに基づいて情報を取得します。

    • Car.java class:このJavaクラスは、各ユーザーにリンクされた属性を持つCarオブジェクトを表します。

      ```
      //Imports
      import org.springframework.stereotype.Component; // For component scanning
      import org.springframework.context.annotation.Scope; // For defining bean scope
      
      @Component // Marks a class as a Spring-managed component
      @Scope("prototype") //A new instance is created every time the bean is requested
      
      public class Car {
      
         //Attributes
      
         private String brand; 
         private String color; 
         private String plate; 
      
         //"getter" and "setter" methods to get and set the information in each object
      
         public String getBrand(){
         return brand;
         }
      
         public void setBrand(String brand){
         this.brand = brand;
         }
      
         public String getColor(){
         return color;
         }
      
         public void setColor(String color){
         this.color = color;
         }
      
         public String getPlate(){
         return plate;
         }
      
         public void setPlate(String plate){
         this.plate = plate;
         }
      
      }
      ```
      
      • House.java class:このJavaクラスは、各ユーザーにリンクされた属性を持つHouseオブジェクトを表します。

        //Imports
        import org.springframework.stereotype.Component; // For component scanning
        import org.springframework.context.annotation.Scope; // For defining bean scope
        
        @Component // Marks a class as a Spring-managed component
        @Scope("prototype") //A new instance is created every time the bean is requested
        
        public class House {
        
           //Attributes
        
           private int floors;
           private String location;
           private int rooms;
           private int bathrooms;
        
        
           //"getter" and "setter" methods to get and set the information in each object
        
           public int getFloors(){
              return floors;
           }
        
           public void setFloors(int floors){
              this.floors = floors;
           }
        
           public String getLocation(){
              return location;
           }
        
           public void setLocation(String location){
              this.location = location;
           }
        
           public int getRooms(){
              return rooms;
           }
        
           public void setRooms(int rooms){
              this.rooms = rooms;
           }
        
           public int getBathRooms(){
              return bathrooms;
           }
        
           public void setBathRooms(int bathrooms){
              this.bathrooms = bathrooms;
           }
        
        }
        
      • User.java class:このJavaクラスは、属性を含むUserオブジェクトを表し、CarおよびHouseオブジェクトを含みます。

        //Imports
        import org.springframework.stereotype.Component; // For component scanning
        import org.springframework.context.annotation.Scope; // For defining bean scope
        
        @Component // Marks a class as a Spring-managed component
        @Scope("prototype") //A new instance is created every time the bean is requested
        
        
        public class User {
        
           //Attributes
        
           private String username; 
           private String userlastname;
           private int id;
           private String email;
           private String date;
           private String attribute;
           private Car car;
           private House house;
        
        
           //"getter" and "setter" methods to get and set the information in each object
        
           public String getUserName(){
           return username;
           }
        
           public void setUserName(String username){
           this.username = username;
           }
        
           public String getUserLastName(){
           return userlastname;
           }
        
           public void setUserLastName(String userlastname){
           this.userlastname = userlastname;
           }    
        
           public int getID(){
           return id;
           }
        
           public void setID(int id){
           this.id = id;
           }        
        
           public String getEmail(){
           return email;
           }
        
           public void setEmail(String email){
           this.email = email;
           }
        
           public String getDate(){
           return date;
           }
        
           public void setDate(String date){
           this.date = date;
           }
        
           public String getAttribute(){
           return attribute;
           }
        
           public void setAttribute(String attribute){
           this.attribute = attribute;
           }
        
           public Car getCar(){
           return car;
           }
        
           public void setCar(Car car){
           this.car = car;
           }
        
           public House getHouse(){
           return house;
           }
        
           public void setHouse(House house){
           this.house = house;
           }
        }
        
  8. ここでは、取得したデータをOCI ATPデータベースに格納するために必要なパラメータをSpring Bootプロジェクトで構成します。resourcesフォルダには、アプリケーションに必要なパラメータの追加に使用するapplication.propertiesファイルが必要です。これは自動的に生成され、アプリケーションの起動時にSpring Bootによってロードされます。

    ノート:パスワードや関連データなどの機密情報をハッカーが抽出または表示できないようにする暗号化およびセキュリティの方法を管理することは非常に重要です。このチュートリアルでは、pom.xmlファイルで構成されたjasyptライブラリを使用します。詳細は、「Jasyptを使用してSpring Bootプロジェクトでパスワードを暗号化する方法」を参照してください。また、Spring BootのJavaクラスでは、application.propertiesパラメータを復号化するために、このライブラリに関連する注釈およびソース・コードを追加する場所について説明します。

    次の図に示すように、ATPデータベースに必要な適切なパラメータをapplication.propertiesファイルに追加します。

    アプリケーション・プロパティ・ファイル

    Spring Boot Javaクラスを作成して、各メッセージをデータベースに格納します。このクラスは、次の図に示すようにdatabaseフォルダにあります。

    データベース・フォルダ

    • SoapObjectRepository.java:このSpring Boot Javaクラスでは、JDBCドライバを使用して、ATPの各メッセージをリアルタイムのトランザクション形式で挿入できます。

      //Java Classes USER, CAR, HOUSE Imports
      import com.oracle_springboot_tutorial.tutorial.model.*;
      
      //Spring Boot Imports
      import org.springframework.stereotype.Repository;
      import org.springframework.jdbc.core.JdbcTemplate;
      import org.springframework.beans.factory.annotation.Autowired;
      
      //Repository Class to save SOAP Messages in the Database
      @Repository
      public class SoapObjectRepository {
         private JdbcTemplate template;
         private User user;
      
         //Getters and Setters for JdbcTemplate template object
         public JdbcTemplate getTemplate(){
            return template;
         }
      
         //Autowired annotation to inject JdbcTemplate object into the template object
         @Autowired
         public void setTemplate(JdbcTemplate template){
            this.template = template;
         }
      
         //Method to save User SOAP Message in the Database
         public void saveUserSOAPMessage(User user){
            this.user = user;
            String sql = "INSERT INTO USERS (username, userlastname, id, email, dateuser, attributeuser) VALUES(?, ?, ?, ?, ?, ?)";
            template.update(sql, user.getUserName(), user.getUserLastName(), user.getID(), user.getEmail(), user.getDate(), user.getAttribute());
         }
      
         //Method to save Car SOAP Message in the Database
         public void saveCarSOAPMessage(Car car){
            String sql = "INSERT INTO CARS (userid, brand, color, plate) VALUES(?, ?, ?, ?)";
            template.update(sql, user.getID(), car.getBrand(), car.getColor(), car.getPlate());
         }
      
         //Method to save House SOAP Message in the Database
         public void saveHouseSOAPMessage(House house){
            String sql = "INSERT INTO HOUSES (userid, floors, locationhouse, rooms, bathrooms) VALUES(?, ?, ?, ?, ?)";
            template.update(sql, user.getID(), house.getFloors(), house.getLocation(), house.getRooms(), house.getBathRooms());
         }
      
      
      }
      

      次に、JSONソフトウェア・コードを追加し、次の図に示すように、最初にjson_messageフォルダとそのJava Spring Bootクラスを作成します。

      Jsonフォルダ

    • JsonBuilder.java:このSpring Boot Javaクラスは、SOAP XML形式からJSON形式に変換します。

      //Imports to be used for JSON
      import org.json.JSONArray;
      import org.json.JSONObject;
      
      //Imports to be used for the User class
      import com.oracle_springboot_tutorial.tutorial.model.*;
      
      
      //Imports to be used for the ArrayList class
      import java.util.ArrayList;
      
      
      public class JsonBuilder {
      
         //The buildJsonMessage method creates a JSON object from the ArrayList of User objects
         public JSONObject buildJsonMessage(ArrayList<User> usersMessageArray) {
      
            JSONObject rootJson = new JSONObject(); //Create a new JSON object called rootJson
            JSONObject messagesJson = new JSONObject(); //Create a new JSON object called messagesJson
            JSONArray messageArray = new JSONArray(); //Create a new JSON array called messageArray
      
      
            //Iterate through the ArrayList of User objects and create a JSON object for each User object in the ArrayList
            for (User user : usersMessageArray) {
                  JSONObject messageJson = new JSONObject();
                  messageJson.put("username", user.getUserName()); //Add the username of the user to the messageJson object
                  messageJson.put("userlastname", user.getUserLastName()); //Add the userlastname of the user to the messageJson object
                  messageJson.put("id", user.getID()); //Add the id of the user to the messageJson object
                  messageJson.put("email", user.getEmail()); //Add the email of the user to the messageJson object
                  messageJson.put("date", user.getDate()); //Add the date of the user to the messageJson object
                  messageJson.put("attribute", user.getAttribute()); //Add the attribute of the user to the messageJson object
      
      
                  //
                  JSONObject bodyJson = new JSONObject(); //Create a new JSON object called bodyJson
                  JSONObject envelopeJson = new JSONObject(); //Create a new JSON object called envelopeJson
      
                  //Switch statement to check the attribute of the User object
                  switch (user.getAttribute()) {
                     case "CAR":
                              Car car = user.getCar();
                              envelopeJson.put("brand", car.getBrand()); //Add the brand of the car to the envelopeJson object
                              envelopeJson.put("color", car.getColor()); //Add the color of the car to the envelopeJson object
                              envelopeJson.put("plate", car.getPlate()); //Add the plate of the car to the envelopeJson object                       
                        break;
                     case "HOUSE":
                              House house = user.getHouse();
                              envelopeJson.put("floors", house.getFloors()); //Add the floors of the house to the envelopeJson object
                              envelopeJson.put("location", house.getLocation()); //Add the location of the house to the envelopeJson object
                              envelopeJson.put("rooms", house.getRooms()); //Add the rooms of the house to the envelopeJson object
                              envelopeJson.put("bathrooms", house.getBathRooms()); //Add the bathrooms of the house to the envelopeJson object                     
                        break;
                     default:
                        System.out.println("Unknown subject: " + user.getAttribute());
                  }
      
      
                  bodyJson.put("envelope", envelopeJson); //Add the envelopeJson object to the bodyJson object
      
                  messageJson.put("body", bodyJson); //Add the bodyJson object to the messageJson object
      
               messageArray.put(messageJson); //Add the messageJson object to the messageArray array
            }
      
            messagesJson.put("message", messageArray); //Add the messageArray array to the messagesJson object
            rootJson.put("messages", messagesJson); //Add the messagesJson object to the rootJson object
      
            return rootJson;
         }
      }
      
  9. これで、JSON形式のメッセージをOCI Queueに送信できるようになりました。次の図に示すように、oci_queueフォルダとそのJava Spring Bootクラスを作成します。

    キュー・フォルダ

    ノート: OCIQueue.javaクラスでは、OKEからOCIキューへのアクセスを定義する必要があります。このチュートリアルでは、ワークロード・アクセスを使用して、テナンシに関連付けられたユーザー、パスワード、OCIDsなどの機密情報を処理することなく、OCIリソースへのアクセス権を付与します。詳細は、「ワークロードのOCIリソースへのアクセス権の付与」を参照してください。

    OCIQueue.javaクラスの開発を開始する前に、テナンシでワークロード・アクセスを構成します。まず、Oracle GraalVMベースのJavaアプリケーションに関連付けるネームスペースを作成する必要があります。管理ホストにいることを確認します。

    kubectl create ns-tutorial
    

    次に、アプリケーションのKubernetesサービス・アカウントを作成します。

    kubectl create serviceaccount tutorialserviceaccount --namespace ns-tutorial
    

    ここで、OCI IAMポリシーを定義して、ワークロードが必要なOCIリソースにアクセスできるようにします。このチュートリアルでは、OCIキューです。

    OCIコンソールに移動し、「アイデンティティとセキュリティ」「ポリシー」に移動して、「ポリシーの作成」をクリックします。次の情報を入力し、「Create」をクリックします。

    • 名前:優先ポリシー名を入力します。
    • 説明: Access from oke to oci queueと入力します。
    • ポリシー・ビルダー:

      Allow any-user to use queues in compartment id ocid1.compartment.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx where all {request.principal.type = 'workload', request.principal.namespace = 'ns-tutorial', request.principal.service_account = 'tutorialserviceaccount', request.principal.cluster_id = 'ocid1.cluster.oc1.sa-saopaulo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'}
      

    ワークロード・アクセス・ポリシー

    ワークロード・アクセス・ポリシー、ネームスペースおよびサービス・アカウントを構成したら、続行できます。

    application.propertiesファイルで、タスク5で作成した特定のOCIキューの接続および管理に必要なキュー・パラメータを追加します。

    OCIID設定

    • OCIQueue.java:このSpring Boot Javaクラスでは、メッセージにアクセスしてOCIキューに配置できます。

      //Imports
      import com.oracle.bmc.auth.okeworkloadidentity.OkeWorkloadIdentityAuthenticationDetailsProvider; //Import OkeWorkloadIdentityAuthenticationDetailsProvider to enable access and use OCI Workload Identity
      import com.oracle.bmc.queue.QueueClient; //Import QueueClient to have access and manage of OCI Queue
      import com.oracle.bmc.queue.model.PutMessagesDetails; //Import PutMessagesDetails to send messages to the OCI Queue
      import com.oracle.bmc.queue.model.PutMessagesDetailsEntry; //Import PutMessagesDetailsEntry to send messages to the OCI Queue
      import com.oracle.bmc.queue.requests.PutMessagesRequest; //Import PutMessagesRequest to send messages to the OCI Queue
      
      //Imports for the ArrayList and List
      import java.util.ArrayList;
      import java.util.List;
      
      public class OCIQueue {
         //Set the required parameters to access to OCI and the Queue
      
         //Variables
         private String queueId; 
         private String endPoint;
         private String region;
      
         //Constructor to initialize the OCI Queue object with the required parameters
         public OCIQueue(String queueId, String endPoint, String region){
            this.queueId = queueId;
            this.endPoint = endPoint;
            this.region = region;   
         }
      
      
         //The sendMessages method sends a message to the OCI Queue
         public void sendMessages(String jsonMessage){
            try{    
      
                  //Create an OkeWorkloadIdentityAuthenticationDetailsProvider object to authenticate the OCI Queue
                  OkeWorkloadIdentityAuthenticationDetailsProvider provider = new OkeWorkloadIdentityAuthenticationDetailsProvider.OkeWorkloadIdentityAuthenticationDetailsProviderBuilder().build();
      
                  //Create a QueueClient object to send the message to the OCI Queue
                  QueueClient queueClient = QueueClient.builder().build(provider);
                  queueClient.setRegion(region);
                  queueClient.setEndpoint(endPoint);
      
      
      
                  //Create a PutMessagesDetailsEntry object to send the message
                  PutMessagesDetailsEntry message = PutMessagesDetailsEntry.builder()
                     .content(jsonMessage)
                     .build();
      
                  //Create a List of PutMessagesDetailsEntry objects to send the message
                  List<PutMessagesDetailsEntry> messages = new ArrayList<>();
                  messages.add(message);
      
                  //Create a PutMessagesDetails object to send the message
                  PutMessagesDetails putMessagesDetails = PutMessagesDetails.builder()
                     .messages(messages)
                     .build();
      
      
                  //  Create a PutMessagesRequest object to send the message
                  PutMessagesRequest putMessagesRequest = PutMessagesRequest.builder()
                     .queueId(queueId)
                     .putMessagesDetails(putMessagesDetails)
                     .build();
      
                  // Send the request and get the response
                  queueClient.putMessages(putMessagesRequest);
      
            }catch(Exception e){
                  System.out.println("Exception sending message to OCI Queue: "+e);
            }
         }
      }
      
  10. データベース、JSONおよびOCI QueueのSpring Boot Javaクラスを取得したら、MessagesEndpoint.javaクラスに進むことができます。

    このために、endpointというフォルダとそのSpring Boot Javaクラスを作成します。

    エンドポイント・フォルダ

    ノート: MessagesEndpoint.javaでは、自動生成されたクラスをインポートする必要があります。これを行うには、pom.xmlファイルの「構成」セクションに次のソースを追加します。

    <configuration>
       <sources>
       <source>${project.build.directory}/generated-sources</source>
       </sources>
    </configuration>
    

    pom.xmlファイルは次のようになります。

    生成されたソース・パス

    • MessagesEndpoint.java:このSpring Boot Javaクラスの目的は、SOAP HTTPリクエストを抽出し、その値をメッセージごとにUser、CarおよびHouse Javaオブジェクトにマップすることです。次に、抽出されたデータをSOAP XMLトランザクションごとにATPデータベースに格納し、データをXMLからJSON形式に変換して、メッセージをOCIキューに配置します。これらのメッセージは、後でコンシューマによってキューから取得および削除できます。

      //Imports
      import com.oracle_springboot_tutorial.tutorial.model.*; //Import all the classes from the model package
      import com.oracle_springboot_tutorial.tutorial.oci_queue.OCIQueue; //Import the OCIQueue class from the oci_queue package
      
      
      import com.tutorial_example.ns0.Messages;//Import the Messages class from the tutorial_example.ns0 package (Auto generated Java Classes from the WSDL)
      import com.tutorial_example.ns0.MessageType; //Import the MessageType class from the tutorial_example.ns0 package (Auto generated Java Classes from the WSDL)
      
      //Import the ArrayList class from the java.util package
      import java.util.ArrayList;
      
      //Spring Boot imports to be used for the SOAP Web Service
      import org.springframework.beans.factory.annotation.Autowired; //Import the @Autowired annotation to inject the SoapObjectRepository object
      import org.springframework.beans.factory.annotation.Value; //Import the @Value annotation to inject the values from the application.properties file
      import org.springframework.stereotype.Component; //Import the @Component annotation to register the class with Spring
      
      //Spring Boot imports 
      import org.springframework.ws.server.endpoint.annotation.Endpoint; //Import the @Endpoint annotation to register the class with Spring WS
      import org.springframework.ws.server.endpoint.annotation.PayloadRoot; //Import the @PayloadRoot annotation to specify the namespace URI and local part of the request payload
      import org.springframework.ws.server.endpoint.annotation.RequestPayload; //Import the @RequestPayload annotation to map the request payload to the method parameter
      import org.springframework.ws.server.endpoint.annotation.ResponsePayload; //Import the @ResponsePayload annotation to map the returned value to the response payload
      
      //Imports to be used storing SOAP information in the database 
      import com.oracle_springboot_tutorial.tutorial.database.SoapObjectRepository; //Import the SoapObjectRepository class from the database package
      
      //Imports to be used for JSON
      import com.oracle_springboot_tutorial.tutorial.json_message.JsonBuilder; //Import the JsonBuilder class from the json_message package 
      import org.json.JSONObject; //Import the JSONObject class from the org.json package
      
      
      //The @Endpoint annotation registers the class with Spring WS.
      //The @Component annotation registers the class with Spring to be used as a Spring Bean.
      @Endpoint
      @Component
      public class MessagesEndpoint {
      
         //Inject not encrypted and decrypted values using jasypt library from the application.properties file
         @Value("${oci.queue.queueId}")
         private String queueId;
         @Value("${oci.queue.endPoint}")
         private String endPoint;
         @Value("${oci.queue.region}")   
         private String region;
         @Value("${spring.datasource.password}")
         private String datasourcePassword;
      
         //The @Autowired loads JDBC template in SoapObjectRepository.
         @Autowired
         private SoapObjectRepository soapObjectRepository = new SoapObjectRepository();
         //Create a new instance of the JsonBuilder class
         JsonBuilder jsonBuilder = new JsonBuilder();
      
      
         //The namespace URI
         private static final String NAMESPACE_URI = "http://tutorial_example.com/ns0";
      
         //The handleMessagesRequest method is annotated with @PayloadRoot, which means that it is invoked when a request with the specified namespace URI and local part is received.
         @PayloadRoot(namespace = NAMESPACE_URI, localPart = "messages")
         //The @ResponsePayload annotation makes Spring WS map the returned value to the response payload.
         @ResponsePayload
         //The handleMessagesRequest method processes the request and sends the message to the OCI Queue.
         public void handleMessagesRequest(@RequestPayload Messages request) {
            OCIQueue ociQueue = new OCIQueue(queueId, endPoint, region);
      
            //Create an ArrayList to store the users
            ArrayList<User> usersMessageArray = new ArrayList<User>();
      
            //Iterate over the messages, extracting the SOAP Messages and storing in the Java Objects (Car, House, User) 
            for (MessageType message : request.getMessage()) {
      
                  User user = new User();
                  user.setUserName(message.getUsername());
                  user.setUserLastName(message.getUserlastname());
                  user.setID(message.getId());
                  user.setEmail(message.getEmail());
                  user.setDate(message.getDate());
                  user.setAttribute(message.getAttribute());
      
                  //Insert User in Oracle ATP
                  soapObjectRepository.saveUserSOAPMessage(user);
      
                  //Process the attributes Car or House depending of the kind of User
                  processMessage(user, message);
                  //Add the user to the ArrayList
                  usersMessageArray.add(user);
            }
      
      
            //Convert to JSON format
            JSONObject jsonObject = jsonBuilder.buildJsonMessage(usersMessageArray);
      
            //Send the JSON message to OCI Queue
            ociQueue.sendMessages(jsonObject.toString());
      
         }
      
         //The processMessage method processes the message based on the user's attribute.
         private void processMessage(User user, MessageType message) {
            String subject = user.getAttribute();
            switch (subject) {
                  case "CAR":
                     handleCAR(user, message);
                     break;
                  case "HOUSE":
                     handleHouse(user, message);
                     break;
                  default:
                     System.out.println("Unknown subject: " + subject);
            }
         }
      
         //The handleCAR method processes the CAR message.
         private void handleCAR(User user, MessageType message) {
            Car car = new Car();
            car.setBrand(message.getBody().getEnvelope().getBrand());
            car.setColor(message.getBody().getEnvelope().getColor());
            car.setPlate(message.getBody().getEnvelope().getPlate());
      
            user.setCar(car);
            //Insert Car in Oracle ATP
            soapObjectRepository.saveCarSOAPMessage(user.getCar());
      
         }
      
         //The handleHouse method processes the HOUSE message.
         private void handleHouse(User user, MessageType message) {
            House house = new House();
            house.setFloors(message.getBody().getEnvelope().getFloors());
            house.setLocation(message.getBody().getEnvelope().getLocation());
            house.setRooms(message.getBody().getEnvelope().getRooms());
            house.setBathRooms(message.getBody().getEnvelope().getBathrooms());
      
            user.setHouse(house);
      
            //Insert Houses in Oracle ATP
            soapObjectRepository.saveHouseSOAPMessage(user.getHouse());
      
         }
      }
      
  11. Spring Bootプロジェクトの構築が完了したので、プロジェクト・フォルダにDockerfileを作成します。

  1. 次のコマンドを実行して、ローカルのDockerリポジトリでプロジェクト・イメージを構築およびプッシュします。

    docker build . -t springbootapp:latest
    

    Dockerイメージの構築

  2. 次のコマンドを実行して、ローカルのDockerリポジトリでイメージを確認します。

    docker images
    

    DockerイメージのローカルRepo

  3. Spring Bootアプリケーション・イメージにOCI Container Registry Classicリポジトリの完全なパスをタグ付けできます。

    docker tag springbootapp:latest gru.ocir.io/xxxxxxxxxx/springboot/tutorialapp:latest
    
  4. 次のコマンドを実行して、ローカルのDockerリポジトリで確認します。

    docker images
    

    OKEアプリケーションのタグ付け

  5. 次のコマンドを実行して、イメージをOCI Container Registry Classicにプッシュします。

    docker push gru.ocir.io/xxxxxxxxxx/springboot/tutorialapp:latest
    

    OKEアプリケーションのプッシュ

  6. OCI Container Registry ClassicでOKEイメージ・アプリケーションを確認するには、「開発者サービス」「コンテナおよびアーティファクト」に移動し、「コンテナ・レジストリ」をクリックします。

    OKE Containe Registryのイメージ

    イメージがOCI Container Registry Classicに入ると、開発環境に移動し、このイメージをOKEにデプロイできます。このチュートリアルでは、次のコマンドを実行して、必要な構成を作成します。

    ノート:ネームスペースおよびサービス・アカウントは以前に構成されているため、シークレットが必要です。

    1. 次のコマンドを実行して、プロジェクト・フォルダにアクセスします。

      cd tutorial/
      
    2. 次のコマンドを実行して、OKEのシークレットを作成します。

      kubectl create secret -n ns-tutorial generic ocir --from-file=.dockerconfigjson=../.docker/config.json --type=kubernetes.io/dockerconfigjson
      

      シークレットOKEの作成

  7. すでにOKE環境が用意されているため、OCI Container Registry ClassicからOKEにアプリケーション・イメージをデプロイします。

    ノート:アプリケーション・イメージをデプロイするには、マニフェスト・ファイルが必要です。このチュートリアルでは、次のyamlファイルがマニフェスト・ファイルであり、アプリケーションのデプロイと、80ポートを使用してリスニングしているOCIロード・バランサで表されるイングレス・サービスの作成に使用されます。

    • springboot_application.yaml:

      apiVersion: apps/v1
      kind: Deployment
      metadata:
      name: soap-oci-queue-app
      namespace: ns-tutorial 
      labels:
         app: soap-oci-queue-app
      spec:
      replicas: 6
      selector:
         matchLabels:
            app: soap-oci-queue-app
      template:
         metadata:
            labels:
            app: soap-oci-queue-app
         spec:
            serviceAccountName: tutorialserviceaccount
            automountServiceAccountToken: true      
            containers:
            - name: soap-oci-queue-app
            image: gru.ocir.io/xxxxxxxxxxxx/springboot/tutorialapp:latest
            ports:
            - containerPort: 8080
            imagePullSecrets:
            - name: ocir-docker-config
      ---
      
      apiVersion: v1
      kind: Service
      metadata:
      name: svc-dev-app 
      namespace: ns-tutorial 
      spec:
      selector:
         app: soap-oci-queue-app
      ports:
         - port: 80
            targetPort: 8080
      type: LoadBalancer
      
  8. マニフェスト・ファイルを保存したフォルダでkubectlコマンドを実行します。

    kubectl apply -f springboot_application.yaml
    

    これで、アプリケーションがデプロイされ、イングレス・ロード・バランサ・サービスがOKEに作成されます。

    OKEでのマニフェストの適用

  9. OKEで作成されたポッドおよびサービスを検証するには、次のコマンドを実行します。

    kubectl get pods -A
    

    OKE発注

    kubectl get svc -A
    

    OKEサービス

    ノート: tutorial.zipからSpring Boot Oracle GraalVMベースのJavaアプリケーション・プロジェクトをダウンロードします。

タスク7: JMeterを使用したSpring Boot Oracle Graal VMアプリケーションのテスト

JMeterインストールの詳細は、JMeterのスタート・ガイドを参照してください。

JMeterがインストールおよび構成されると、HTTP POST SOAPリクエストを送信できます。たとえば、2つの同時ユーザーまたはアプリケーションを表すスレッド数を2に設定し、「ループ数」を3000に設定します。つまり、各ユーザーまたはアプリケーションが合計6000のSOAPリクエストに対して3000リクエストを送信します。

SOAPメッセージ

JMeterで、OCIロード・バランサIP、Spring Bootプロジェクトで構成されたパス、および本文にSOAP XMLを設定します。

HTTP SOAPリクエスト

6000 SOAPトランザクションでJMeterを実行し、確認します。

ノート:クライアント・アプリケーションのSOAPメッセージングをシミュレートしているため、SOAP HTTPリクエストごとに、情報は前述のSOAP XMLファイルに表示される情報と同じであり、変更されませんが、実際の顧客環境では情報は確実に異なります。

次の文を実行します。

  1. ATPに保存されているデータの合計を表示します。

    Oracle ATP

  2. ATPの各表に格納されているデータの詳細を表示します。

    • CARS:

      CARS

    • 住宅:

      住宅

    • ユーザー:

      ユーザー

  3. OCIキューに格納されているOCIキュー・リクエストの合計を確認します。

    OCIキュー・リクエスト合計

  4. OCIキューのメッセージ詳細をJSON形式で表示します。

    OCIキュー・メッセージ詳細

承認

その他の学習リソース

docs.oracle.com/learnの他のラボを確認するか、Oracle Learning YouTubeチャネルで無料のラーニング・コンテンツにアクセスしてください。また、education.oracle.com/learning-explorerにアクセスしてOracle Learning Explorerになります。

製品ドキュメントについては、Oracle Help Centerを参照してください。