DataSpiderSDKによるスマートコントラクトアダプタの作成

スマートコントラクトアダプタの作成 その１

今回から、DataSpiderSDKを用いてスマートコントラクトアダプタの作成例について紹介します。

## なにをやるか
[前回までの記事では、ブロックチェーンとNFT連携の実現](https://qiita.com/dataspider-fan)に向けて紹介してきました。
実現したいことに対して実装をしてきたため、本来のパッケージアプリケーションソフトウェアとしてのアダプタとしては汎用性の低い実装となっていました。
今回からアダプタのブラッシュアップとして、汎用的なスマートコントラクトアダプタの実装とDataSpiderSDKが提供する機能について掲載します。
参照系のスマートコントラクト関数呼び出しを前提に紹介します。

## 実装方針
前回までの実装内容から、基本的にべた書きになっていた部分を動的に設定できるようにします。
基本的な実装コンセプトは、利用者が使用する機能に必要な情報を動的に指定できるようにするということです。

## 課題と改善点
1. TRONネットワークへの接続先が固定だったため、グローバルリソースモジュールを用いて接続先を動的に変更できるようにします。
2. 呼び出すスマートコントラクトが固定だったため、スマートコントラクトアドレスの入力欄を設けて指定したスマートコントラクトへアクセスできるようにします。
3. 呼び出すスマートコントラクト関数が固定だったため、指定したスマートコントラクトから関数情報を取得し指定できる設定項目を設けます。
4. スマートコントラクト関数と入出力するための項目が固定だったため、UIのテーブルモジュールを用いて必要な項目を追加、定義できるようにします。
5. その他、内部的に処理については、DataSpiderのお作法に馴染ませるように実装をすすめます。

## DataSpiderSDKの主要な利用機能
接続先（エンドポイント）を動的に設定可能とするのがグローバルリソースです。
設定プロパティ画面にグローバルリソースを選択できる項目を設け実装します。

:::note
グローバルリソースとは、すべてのプロジェクトから利用できる共有リソースです。データベースやメールサーバ、FTP サーバなど外部システムへの接続はグローバルリソースとなります。グローバルリソースはシステムで非常に
重要なリソースとなります。
※ DataSpider Servista Component SDK 4.2 Getting Started ガイド第７章グローバルリソースについてより引用
:::

## 画面イメージ
1. グローバルリースの画面イメージ
![グローバルリソース_1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849535/a50d5e6c-8abc-0f54-0de2-cacbe1c7879b.png)
接続先設定欄を設けます。また、スマートコントラクトのアドレス、利用者の秘密鍵も併せて設定できるようにします。
![グローバルリソース_2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849535/47d6d34c-3451-e95e-b30d-2dbe9ffb51d6.png)

2. 参照系スマートコントラクトアダプタの設定画面
「接続先」欄にてグローバルリソースの設定項目を設けました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849535/f30935ec-7e4c-7597-b7bf-89a6dc231f2a.png)
関数名は、上記で入力したスマートコントラクトより取得し一覧から選択できるようにしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849535/40fde7c9-58bc-2afc-6b5d-2960140bce60.png)
参照系スマートコントラクトが使用する入力側項目を設定できるテーブルを設けました。
![参照系_2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849535/7e6a6250-dccb-fc6b-5b8d-5655f627e3a9.png)
出力側も同様です。参照系スマートコントラクトの戻り値（出力側パラメータ）を出力する項目設定です。
![参照系_3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849535/e088c2dd-25b7-4578-a89a-f034fb5f6eb5.png)
実行時の設定項目として、そのつど処理タイミングでアカウントを切り替える、さらにスマートコントラクトを選択できるようにしました。
![参照系_4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849535/46b1f22b-a319-dd10-ac23-a055c1d31dce.png)

## DataSpiderSDKによるグローバルリソースの実装
アダプタに対応するグローバルリソースの実装にあたり、SDKのサンプルコードとして提供がされていました。

サンプルソース：sample_resource_adapter
AdapterModuleComponent内でResourceFactoryのインスタンスを生成することにより、アダプタからグローバルリソースのUIを呼び出すことができます。
今回のスマートコントラクトアダプタの実装においても同様の作法に従いました。

### ソースコードの構成
スマートコントラクトアダプタのblockchain_tron配下に、ResourceFactoryを置きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849535/8920a87b-4805-bdc2-d417-2207fffe9139.png)

#### グローバルリソースの実装例
TronAdapterModuleComponent内で、BlockchainTronGlobalResourceFactoryインスタンスを生成します。

```java
package com.jpn.linux.dp.modules.adapter.Tron;

import com.appresso.ds.dp.spi.AdapterModuleComponent;
import com.appresso.ds.dp.spi.OperationFactory;
import com.appresso.ds.dp.spi.ResourceFactory;

public class TronAdapterModuleComponent extends AdapterModuleComponent {


	@Override
	// リソースファクトリのインスタンスを配列で返します。
	public ResourceFactory[] getResourceFactories() throws Exception {
		return new ResourceFactory[] {
				new BlockchainTronGlobalResourceFactory()
		};
	}

	@Override
	public OperationFactory[] getOperationFactories() throws Exception {
		return new OperationFactory[] {
				new GetTronOperationFactory()
		};
	}
}
```

あとは、BlockchainTronGlobalResourceFactory内でMetaDataを定義することよりよりグローバルリソースに表示されるようになります。
```java
	@Override
	public ResourceMetaData getResourceMetaData(
			ResourceContext context) throws Exception {

		// ResourceMetaData を生成
		ResourceMetaData meta = new ResourceMetaData(this, context);
		// リソースのアイコン名を設定
		meta.setLabel("Tron接続設定(TronGrid)");
		meta.setDefaultType(ResourceInfo.TYPE_GLOBAL_RESOURCE);
		// プールのサポートを有効に設定
		meta.setPoolSupported(true);
		meta.setPoolDisabled(false);
		return meta;
	}

```

#### スマートコントラクト呼び出しと入出力マッピング部分の実装例
##### GetTronOperation.java
```java
	public Map execute(Map inputData) throws Exception {
		Map res = new HashMap();

		LoggingContext log = getContext().log();

		Type[] smartContractFunctionInputParams;
		Column outCol;
		String outColValue;
		String smartContractResult;

		int outputSize = this.outputMappingItems.length;

		LocalDateTime now = LocalDateTime.now();
		DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy/MM/dd");

		Object data = inputData.get(KEY_INPUT);

		// 出力側TableDataBuilderを生成する。
		TableDataBuilder builder = DataBuilderFactory.newTableDataBuilder(getColumnOutputTypes(), isLargeData());

		log.info(builder.toString());

		try (TableRowIterator rowIterator = DataParserFactory.newTableRowIterator(data)) {

			// 出力側項目の出力開始を宣言する
			builder.startConstruction();

			// 入力側レコードの繰り返し処理
			while (rowIterator.hasNext()) {
				Row record = rowIterator.next();

				builder.startRow(); // 出力側行単位のデータを格納するバッファ

				/* スマートコントラクトの引数を生成
                *  入力側の項目を読み取りスマートコントラクトに渡す引数を生成します
                *  転送する内容は、はあらかじめ入力側の情報をもとに生成しておきます
                */
				smartContractFunctionInputParams = getSmartContractFunctionInputParams(
													record,
													this.inputItemIndex,
													this.inputSmartcontractItemIndexType);

				/* 生成したスマートコントラクト引数を渡しスマコン関数の実行
                *  生成した引数をスマートコントラクト関数を呼び出し、スマコン関数を実行します
                *  
                */
				smartContractResult = doSmartContract(smartContractFunctionInputParams);

				/* スマートコントラクトの戻り値を含め出力側バッファに転送します
                *  転送する内容は、はあらかじめ入力側および出力側の情報をもとに生成しておきます
                */
				for (int i = 0; i < outputSize; i++) {
					if (this.outputMappingItems[i].type == OUTPUT_TYPE_NONE ) {
						builder.column("");
					} else if (this.outputMappingItems[i].type == OUTPUT_TYPE_FROM_INPUT ) {
						outCol = record.getColumn(this.outputMappingItems[i].fromIndex);
						outColValue = getColumnValue(outCol);
						builder.column(outColValue);
					} else if (this.outputMappingItems[i].type == OUTPUT_TYPE_FROM_SMRESULT ) {
						builder.column(smartContractResult);
					}
				}

				builder.endRow();

			}

			builder.endConstruction();
			Object result = builder.getResult();

			res.put(KEY_OUTPUT, result);

		}

		return res;
	}

```

## スクリプト実装例
1. スクリプトメイン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849535/b90915d4-3e89-9034-c6d0-45b0dab07dac.png)
2. 入力側マッピング
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849535/6bb04383-7153-2e15-5911-96e5bfc20fbd.png)
3. 出力側マッピング
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849535/167d9aba-3647-22d9-f3fa-32ddfb27f520.png)
※ 入力側項目を出力する設定が有効な場合

## 次回は
[更新系のスマートコントラクト関数呼び出しのためのアダプタ](https://qiita.com/dataspider-fan/items/1363631e187b2bf59e04)について紹介したいと思います。