iDempiere（JPiere）のプロセス機能の実装

前回までの内容で、iDempiere（JPiere）の[動作環境の構築](https://qiita.com/idempiere-fan/items/ca8520f567c5939cc5cc)および[開発環境](https://qiita.com/idempiere-fan/items/2d8fd80d2f80f2478e4f)が整いました。
今回から、iDempiere（JPiere）に搭載している機能を用いて動作確認や機能追加カスタマイズ（実装）していきます。

今回はProcess機能に焦点をあて、独自のProcess機能を実装してみたので、その実装手順と検証内容を紹介したいと思います。

## iDempiere（JPiere）のProcess機能の種類
Process機能はSOAP通信もしくはRESTful APIを使用して外部システムから実行することができます。
また、画面のメニューに登録することによりWAS経由で画面から実行することもできます。
今回は、SOAP通信を使用して外部システムから起動するというシナリオを想定し、ProcessおよびWebService連携について焦点をあてます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/b59c28e0-f3d1-815c-93d8-70830eaaba46.png)

## 実装するProcess機能
今回実装するProcess機能は、2つの引数を受け取り（値はデバッグ画面に表示）処理結果のメッセージを返却するというシンプルな処理です。
実装はJava言を使用し、iDempiereでの画面設定をへて、SOAP通信によりプロセスを実行します。
なお、Process機能の動作確認するため、SoapUIオープンソース版を使用します。

### 実装から動作確認までの手順
今回の動作検証は、以下の手順ですすめます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/93abdde3-f847-ebb9-26a2-67228efb1aac.png)

### 実装後のソース構成
これから順番に実装内容を紹介しますが、以下の手順でソースコードを追加した結果、以下のファイル構成になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/175062c9-9d6e-dc4e-81ec-dd3b753a3ad7.png)


## 参考ページ
以下の技術文書を参考にさせていただきました。

https://wiki.idempiere.org/en/Process

https://wiki.idempiere.org/en/Web_Services_First_Steps

https://idempiere.atlassian.net/browse/IDEMPIERE-5001


## Process機能の実装
それでは、Process機能を実装していきます。
まずは、Process機能をEclipseのプラグインウィザード(Plug-in Project)から実装していきます。

### Plug-in Projectの追加
ProjectExplorerから新規作成を選択し、プラグインウィザード(Plug-in Project)をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/593bcc48-6cc2-2297-b180-99dffa825057.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/b13d2078-e149-122a-fe6b-a691f136786e.png)
org.idempierefan.plugin.processを追加します。
また、ソースコードを配置するLocationを指定します。併せてOSGIフレームワークも有効にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/f2274b99-b6d1-54ad-0c61-9cf9032b3cd2.png)
Java11が選択されていることを確認し、Finishボタンにより登録します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/cb05cda5-880c-b154-2fca-3be5c5a6f8fc.png)

Overview画面が表示されれば、Plug-in Projectの追加は完了です。
※ MANIFEST.MFファイルの内容
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/81ec4fa1-3db0-22ea-67f2-430598fd4d9b.png)
つづいて、org.idempierefan.plugin.processの設定を行います。

### Plug-inに関連するパッケージの追加
Dependenciesタブをクリックし、Addボタンから、```org.adempiere.base```を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/97f30b5c-e59e-a084-ec16-13f5ac38908a.png)

つづいて、Run Configurationsにorg.idempierefan.plugin.processを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/1fabdeb6-2f2d-0da7-94c7-db35043d8ff3.png)
server.productをひらき、```Plug-insタブ```から```org.idempierefan.plugin.process```のチェックボックスをONにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/3339af92-0e58-5ccc-0fc7-062d41a351f1.png)
ここまでで、初期のPlug-inの設定は完了です。
それでは、Process機能の実装にはいります。

### Process機能ソースコード実装
Process本体の実装とProcess機能を提供するFactoryの実装を行います。

#### Process本体の実装手順
最初にProcess機能本体のパッケージを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/653f0259-7c7a-4ff8-5b38-e4ed2062a569.png)
Nameに```org.idempierefan.plugin.process```を入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/41a2c374-7807-84dc-5469-e857bc829d4f.png)
つづいて、本体のクラスを追加します。
ここで指定するクラス名は、画面から起動する際に設定するプロセスの指定クラス名になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/72fd03a6-8110-0c10-ad7b-ba02914b2e64.png)
```CalcProcess```という名称にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/e5ab5b6f-77a7-382a-4d7b-9baa46c82888.png)
※ 追加したクラスの初期状態
```java
package org.idempierefan.plugin.process;

public class CalcProcess {

}
```
具体的な実装を行います。
CalcProcessは、SvrProcessを拡張する形で実装します。
基本的には、prepareおよびdoIt関数内にコードを記述していきます。
**prepare関数：引数を受け取る処理を記述**
**doIt関数：業務ロジックの処理を記述**
※ 実装途中の状態
```java
package org.idempierefan.plugin.process;

import org.compiere.process.SvrProcess;

public class CalcProcess extends SvrProcess {

	@Override
	protected void prepare() {
		// TODO Auto-generated method stub
		
	}

	@Override
	protected String doIt() throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

}
```
#### CalcProcessクラスの実装結果
```prepare()```関数で```argValue1```および```argValue2```のパラメータをクラスメンバ変数に格納します。
```doIt()```関数で、値が受け取れたことをデバッグ表示し、```Post values SUCCESS!```を返却します。
SOAP通信のレスポンスに```Post values SUCCESS!```がセットされます。
```java
package org.idempierefan.plugin.process;

import org.compiere.process.ProcessInfoParameter;
import org.compiere.process.SvrProcess;

public class CalcProcess extends SvrProcess {

	private String value1;
	private String value2;
	
	@Override
	protected void prepare() {
		
		log.warning("[x] prepare method.");
		
		ProcessInfoParameter[] params = getParameter();

		for (ProcessInfoParameter p : params) {
			
			String paramName = p.getParameterName();
			
			if (paramName.equalsIgnoreCase("argvalue1")) {
				value1 = p.getParameterAsString();
				log.warning(" [-] Found argValue1 Parameter.");
			} else if (paramName.equalsIgnoreCase("argvalue2")) {
				value2 = p.getParameterAsString();
				log.warning(" [-] Found argValue2 Parameter.");
			}
			// TODO : Error handling for unexpected parameters.
			
		}

	}

	@Override
	protected String doIt() throws Exception {
		
		log.warning("[x] doIt method.");
		
		log.warning(value1 != null ? " [-] argValue1: " + value1 : "argValue1 NULL");
		log.warning(value2 != null ? " [-] argValue2: " + value2 : "argValue2 NULL");
		
		return "Post values SUCCESS!";
	}

}
```

### Process機能Factoryソースコード実装
つづいて、Process機能のFactoryを実装します。
上記CalcProcessクラスとは別のパッケージを用意します。
* パッケージ名：org.idempierefan.plugin.processfactory
* FactoryClass：ProcessFactoryImpl

なお、今回実装するFactoryクラスは、AnnotationBasedProcessFactoryを拡張します。
基本的には、getPackages関数内にコードを記述していきます。
```@Component```アノテーションも対象のインターフェースクラスおよび優先順を技術文書通りに付与します。
**getPackages関数：Processクラスのパッケージ名を記述**

#### ProcessFactoryクラスの実装結果
```java
package org.idempierefan.plugin.processfactory;

import org.adempiere.base.AnnotationBasedProcessFactory;
import org.adempiere.base.IProcessFactory;
import org.osgi.service.component.annotations.Component;

@Component(immediate = true, service = IProcessFactory.class, property = {"service.ranking:Integer=0"})
public class ProcessFactoryImpl extends AnnotationBasedProcessFactory {

	public ProcessFactoryImpl() {
	}

	@Override
	protected String[] getPackages() {
		return new String[] {"org.idempierefan.plugin.process"};
	}

}
```

### ProcessFactoryクラスのOSGIコンポーネット定義の作成
実装したProcessのインスタンスがプロセスとして認識できるようにFactoryを実装します。
META-INFと同じ階層に、OSGI-INFフォルダを作成します。
フォルダを作成したら、org.idempierefan.plugin.processfactory.ProcessFactoryImpl.xmlファイルをComponent Definitionにより追加し、定義情報を記述します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/2bd0a606-fa9d-9ede-26b1-c89130f6023e.png)

#### org.idempierefan.plugin.processfactory.ProcessFactoryImpl.xml定義
以下の内容を、```Source```タブを開いて貼り付けます。
```xml
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="org.idempierefan.plugin.processfactory.ProcessFactoryImpl">
   <property name="service.ranking" type="Integer" value="0"/>
   <service>
      <provide interface="org.adempiere.base.IProcessFactory"/>
   </service>
   <implementation class="org.idempierefan.plugin.processfactory.ProcessFactoryImpl"/>
</scr:component>
```
※ EclipseのSource画面内
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/a68ead4c-1df2-92bc-a703-d3c8bbc939cb.png)



#### フォルダ構成 ※ 再掲載
上記手順により、Process本体およびFactoryのコードを記述した結果、以下の状態になっていると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/c911ee89-09a6-e67d-7040-e1d11f3435b9.png)

#### OSGI-INFフォルダビルド対象追加
OSGI-INFフォルダをビルド対象に追加します。
MANIFEST.MFファイルを開き、Buildタブ内の設定で```OSGI-INF```のチェックをONにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/6c071cea-0f74-16bf-56a8-26ae4ff8c1e1.png)

ソースコードおよび定義ファイルの追加は以上です。

ここまで追加したソースコードが正しく動作するか、iDempiereのサービスを起動します。
Eclipseから```Run Configurations```を開き、Runボタンによりサービスを起動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/1c59ad70-0f7b-784f-3f84-ccb82ef96a54.png)

## iDempiere画面からのプロセス機能の定義
続いて、iDempiereの画面からProcessとして上記のCalcProcessパッケージを認識させます。

### ログイン
Process機能を登録する場合は、Admin権限でiDempiereにログインします。
```https://localhost:8443/webui```にてメインページを開きます。
Select Roleを有効にします。
**EMail：superuser@oss-erp.co.jp**
**Password：System**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/22404981-6c98-4f4e-be10-f7999cd238f3.png)
**Tenant：System**
**Role：System Administrator**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/27ce6b54-d86d-4c52-e6a7-26976fb2eccc.png)

### Process機能CalcProcessの登録
メニュー左側から```Report & Process```をクリックします。
新規アイコンをクリックし、Process登録画面を表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/e2c78aea-d9dc-ec41-63eb-c71375095ffb.png)

以下の各項目を入力します。
**Search Key：CalcProcess**
**Name：CalcProcess**
**Description：CalcProcess**
**Data Access Level：All**
**Classname：org.idempierefan.plugin.process.CalcProcess**
Classnameは、実装したソースコード名に合わせて入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/1e572f8c-4005-2777-464e-583c9d0564f0.png)
入力したら画面上部の保存ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/e8d124eb-5463-9e0c-0f67-70b9a6602cff.png)
続いて、CalcProcessのパラメータを登録します。

### CalcProcessの引数の登録
画面下にある```Parameter```タブから引数を２つ登録します。
こちらは、上記ソースコードで実装した文字列を指定します。
なお、ここで指定する項目名称は、SOAP通信時のリクエストに含めます。項目名称と大文字小文字を区別するので注意します。
型はStringを指定します。
**１項目目：argValue1**
**２項目目：argValue2**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/81b95da3-9978-af8f-6088-3e143987d115.png)
追加したら、表の上部にある保存ボタンをクリックします。
以上で、追加したパッケージおよびプロセスがもつ引数について定義が完了しました。

## iDempiere画面からのWebサービスセキュリティの設定
つづいて、SOAPリクエストからこのProcessを実行するためWebServiceのセキュリティ設定を行います。
Webサービスセキュリティの設定は、```GardenWorld Admin```ロールを使用して設定を行います。
### ログイン
**EMail：superuser@oss-erp.co.jp**
**Password：System**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/f5355da6-1baf-2b72-8b79-64f58687e358.png)
**Tenant：GardenWorld**
**Role：GardenWorld Admin**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/c5c545fd-02aa-41f9-5b09-40944e439a62.png)

### Web Service Securityの設定
* ログイン後、画面上部からWeb Service Security設定を検索します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/4180aae6-77d1-29f3-a0bc-3d53ba88d543.png)

:::note info
色々な機能や画面を検索する場合が多い方は、まずはロケール英語でログインして操作することをお勧めします。
:::

* 新規ボタンを押し、それぞれ設定項目を入力します。
CalcProcessを呼び出す名称は```WebServiceCalcProcess```とします。
Web Serviceには、Model Oriented Web Servicesを有効にし、Run Processを選択します。
なお、ここで指定した名称を、後続の動作確認時のSOAPリクエストに指定します。
**Search Key：WebServiceCalcProcess**
**Name：WebServiceCalcProcess**
**Web Service：Model Oriented Web Services**
**Web Service Method：Model Oriented Web Services_Run Process**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/5fd6db72-afef-3cef-7131-9658955f838b.png)

* つづいて、パラメータ（Web Service Parameters）を指定します。
ここでは、iDempiereの内部で定義されている情報を明示的に登録します。※ CalcProcessの引数とは別の定義です。
```Create Default Parameter```ボタンをクリックします。
クリックすると、画面下部のリストに3項目表示されます。
ParameterTypeを全て```Free```にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/52bb5439-b26d-3b20-edff-0f3ea498b6d5.png)
登録後、保存ボタンをクリックします。

::: note warn
これらのパラメータは画面上では特に値を指定しませんが、iDempiereの仕様上必ずこの3種類をFreeとして定義します。
定義が無い場合や、Free以外の設定がされている場合、SOAPからのプロセス実行のリクエストが失敗します。
:::

* つづいて、アクセス権限（Web Service Access）にRole情報を設定します。
となりのタブに```Web Service Access```があります。こちらをクリックします。
なお、ここで指定したRole情報をSOAPリクエストに数値として指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/d98858d4-64b2-cf3c-eb22-b47c2051c72a.png)

ここまで、設定ができたらiDempiereサービスを再起動します。
Eclipseに戻りサービスを停止します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/12838f25-aee4-7b85-f4aa-8c7f10e5ee33.png)

再度、Runメニューの```Run Configurations```から実行します。
SOAP経由でリクエストを受信する設定は以上です。

## SOAPリクエストの準備
SoapUIというツールを使用して、SOAPデータをiDempiereサービスにリクエストを送信します。
SOAP通信なので、wslを最初に認識させます。その後、プロセス実行に必要な引数を指定していきます。
* SoapUIを起動します。
起動後、```New SOAP Project```をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/e78ee6ff-dcc1-907f-1bff-f2f0e62d710f.png)

* WSL情報を読み込みます。
Initial WSDLに```http://localhost:8080/ADInterface/services/ModelADService?wsdl```を指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/e5d239b2-b57c-572c-ab4a-fb33b12d4388.png)

* CalcProcessの起動情報とパラメータを指定します。
起動後、ツリーから```RunProcess```を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/75b2f14c-3fbe-3924-10f0-8da131142d2f.png)

* CalcProcessへアクセスする情報を指定します。
```xml
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:_0="http://idempiere.org/ADInterface/1_0">
   <soapenv:Header/>
   <soapenv:Body>
      <_0:runProcess>
         <_0:ModelRunProcessRequest>
            <!-- AD_Process_IDにCalcProcessのProcessコードを指定する -->
            <_0:ModelRunProcess AD_Process_ID="1000352">
               <!-- WebServiceCalcProcessを指定する-->
               <_0:serviceType>WebServiceCalcProcess</_0:serviceType>
               <_0:ParamValues>

                  <!-- argValue1に値を指定する -->
                  <_0:field column="argValue1" >
                     <_0:val>123</_0:val>
                  </_0:field>

                  <!-- argValu2に値を指定する -->
                  <_0:field column="argValue2" >
                     <_0:val>456</_0:val>
                  </_0:field>
                  
               </_0:ParamValues>
            </_0:ModelRunProcess>
            <_0:ADLoginRequest>
               <!-- 以下に接続情報を指定する -->
               <_0:user>superuser@oss-erp.co.jp</_0:user>
               <_0:pass>System</_0:pass>
               <_0:lang>en_US</_0:lang>

               <!-- GardenWorld -->
               <_0:ClientID>11</_0:ClientID>
               <!-- GardenWorld Admin -->
               <_0:RoleID>102</_0:RoleID>
               <!-- GardenWorld HQ! -->
               <_0:OrgID>11</_0:OrgID>
               <!-- HQ Warehouse -->
               <_0:WarehouseID>103</_0:WarehouseID>
               
               <_0:stage>0</_0:stage>
            </_0:ADLoginRequest>
         </_0:ModelRunProcessRequest>
      </_0:runProcess>
   </soapenv:Body>
</soapenv:Envelope>

```
上記でリクエスト送信の準備は完了です。

## 動作確認
動作確認は、上記SOAPリクエストをSoapUIから送信します。
左側の三角ボタンをクリックするとリクエストが送信されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/b798f3ee-2207-7d90-64e7-7df5fa895daa.png)
画面右側にレスポンスが表示されます。
Summary項目に、ソースコードで指定した文字列が返却されることが確認できます。
```xml
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <ns1:runProcessResponse xmlns:ns1="http://idempiere.org/ADInterface/1_0">
         <RunProcessResponse IsError="false" xmlns="http://idempiere.org/ADInterface/1_0">
            <Error>Post values SUCCESS!</Error>
            <Summary>Post values SUCCESS!</Summary>
            <LogInfo/>
         </RunProcessResponse>
      </ns1:runProcessResponse>
   </soap:Body>
</soap:Envelope>
```
また、Eclipseのコンソールウィンドウからもデバッグ情報としてパラメータが受け取れたことが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/c3c5488e-9653-8a27-2f8e-5dc56e4d296d.png)

実装と動作確認は以上です。

## 残課題
* レスポンスの形式検証
今回は、doIt関数から文字列を返却するというシンプルなレスポンス形式です。
実運用では複雑なレスポンスなど想定されますが、こちらは別途検証が必要だと思われます。
* PCalcProcess内からのデータベースや他インスタンスへのアクセス
受っとった文字列をキーにして、データベースと連動できるような機能も有用かなと思われますが、DBアクセスなどは今回は検証しておりません。

## 次回は
今回はSOAP経由でCalcProcessを呼び出し実行しました。
このProcessは、画面からのボタンと連動することもできます。
次回は、[画面のメニューにボタンを配置して実行する](https://qiita.com/idempiere-fan/items/35a9a871b6f33f102d30)内容を紹介したいと思います。