AWS LambdaからSAP Jco経由でABAPを呼び出す

f:id:yomon8:20180710082903p:plain

タイトルの件、調査がてら書きました。

デプロイ方法

レポジトリ取得

github.com

$ git clone https://github.com/yomon8/aws-lambda-hello-sap.git

SAP JCoの準備

動かすにはがSAP JCo必要ですが、リポジトリに含めるのはまずいので除いています。この記事読んでる人ならSユーザ持ってる人多いと思うので、こちらからダウンロードしてください。

必要なのは Linux (for Intel compatible processors)64-bit x86 です。

support.sap.com

ダウンロードしたファイルを解凍すると以下のようなファイルが入っているはずです。 

$ ls -1
Readme.txt
examples
javadoc
libsapjco3.so
sapjco3.jar
sapjcomanifest.mf

sapjco3.jarlibsapjco3.so  を先程Cloneしたリポジトリのlibフォルダに配置します。

$ ls lib/
libsapjco3.so  sapjco3.jar

ビルド

Gradleでビルドすると、jarファイルが生成されます。

$ gradle build
$ ls build/libs/aws-lambda-hello-sap.jar
build/libs/aws-lambda-hello-sap.jar

AWS Lambda作成

AWS Management ConsoleからJava8でAWS Lambdaを作成します。特別なロールは不要です。VPC等は適切に設定してください。

f:id:yomon8:20180709235732p:plain

jarファイルをアップロードする際にはハンドラに、com.example.hellosap.HelloSAP::handleRequestと設定します。

f:id:yomon8:20180709235735p:plain

テスト用のイベントを作ってみます。

f:id:yomon8:20180709235738p:plain

作成したイベントを使ってLambdaを呼び出したところSAPから応答が帰ってきているのがわかります。

f:id:yomon8:20180710084656p:plain

SAMでローカルテストとデプロイ

リポジトリのルートにあるtemplate.yaml を準備しているので、SAPとネットワーク繋がる環境にローカル環境があるなら、ローカル環境でLambdaのテストもできます。

$ echo '{"host":"YOUR ABAP HOST","sysnum":"00","client":"800","user":"DDIC","password":"19920706"}' | sam local invoke
 
--省略--

{"echoText":"Hello SAP","respText":"SAP R/3 Rel. 740   Sysid: SID      Date: 20180709   Time: 151017   Logon_Data: 800/DDIC/J"}

--省略--

当然、そのままSAMでデプロイも可能です。

$ aws cloudformation package \
  --s3-bucket ${S3_STAGING_BUCKET} \
  --s3-prefix ${S3_STAGING_KEY} \
  --template-file ./template.yaml \
  --output-template-file  ./packaged.yaml
$ aws cloudformation deploy \
  --template-file ./packaged.yaml \
  --stack-name hello-sap \
  --capabilities CAPABILITY_IAM

ポイント

引っかかったりした部分を並べておきます。

最新のライブラリ

AWSデベロッパーガイド等を見ていたのですが、結構内容古いです。ライブラリもMavenリポジトリ直接確認した方が良いと思われます。

https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.amazonaws%22%20AND%20a%3A%22aws-lambda-java-core%22

POJOを利用したデータを受け渡し

JSONでログオンデータを渡してましたが、こちらを参考に実装していま。

例: ハンドラーの入出力に POJO を使用する (Java) - AWS Lambda

Gradleにjarやsoを含める

今回はVisual Studio Code使って作りましたが、libファイルに置いたjarは以下の設定で読み込めるようになります。

dependencies {
    compile 'com.amazonaws:aws-lambda-java-core:1.2.0'
    compile files('lib/sapjco3.jar')
}

加えて以下の設定をbuild.gradleに入れることで、生成されるjarファイルにsapjco3.jarlibsapjco3.so を含められるようになりました。

jar {
  into('lib') {
    from 'lib'
  }
  from {
    configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
  }
}

VPCでは注意が必要

JCoは通信時に内部的にローカルホストの解決をしようとするみたいなのですが、VPCにENIをおろしているとローカルホストの解決が正しくできないので、エラーを吐いてしまっているようです。

例えば下の例で言えば、 `ASHOST=192.168.1.2` を相手に指定しているのに、何故か `hostname 'ip-10-11-180-183' unknown` とかローカルのホストの解決でエラーを吐いてしまいます。

START RequestId: 8e59bdfd-8a2c-11e8-b45a-9577fea6fb56 Version: $LATEST
Start192.168.1.229Result:trueip-10-11-180-183: ip-10-11-180-183: Name or service not knowncom.sap.conn.jco.JCoException: (102) JCO_ERROR_COMMUNICATION: Initialization of repository destination  failed: Connect to SAP gateway failed
Connection parameters: TYPE=A DEST="<Java Rfc client>" ASHOST=192.168.1.2 SYSNR=00 GWHOST=192.168.1.2 PCS=1

LOCATION    CPIC (TCP/IP) on local host with Unicode
ERROR       hostname 'ip-10-11-180-183' unknown
TIME        Wed Jul 18 01:47:56 2018
RELEASE     721
COMPONENT   NI (network interface)
VERSION     40
RC          -2
MODULE      nixxhl.cpp
LINE        193
DETAIL      NiHLGetNodeAddr: hostname cached as unknown
COUNTER     2
END RequestId: 8e59bdfd-8a2c-11e8-b45a-9577fea6fb56
REPORT RequestId: 8e59bdfd-8a2c-11e8-b45a-9577fea6fb56    Duration: 7332.38 ms    Billed Duration: 7400 ms     Memory Size: 128 MB    Max Memory Used: 90 MB

これは、Route53等の内部DNSで名前引きできる環境だとエラー出ずに通信できます。