前回、SWFの概要を説明しましたが、きっと私の説明力不足により
よくわからなかったと思うので、今回からは実際にApexからSWFを触ってみて挙動を確認します。
ApexでSWFを動かすには例のごとくSDKは存在しないので、
直接REST APIをコールする必要があります。
ということで、今日はREST APIをApexからコールするところのベース(Signature生成)を説明します!
さて、AWS REST API恒例のSignature生成ですが、
SimpleDBのSignature Version2やDynamoDBのSignature Version4と異なり、
Signature Version3という署名認証方式で行われます。
Signature Version3は以下の形式で生成します。
Canonicalヘッダ =
"ヘッダ1:値1\n" +
"ヘッダ2:値2" + ...;
string_to_sign =
"HTTPメソッド名\n" +
"リソースのパス\n" +
"クエリパラメータ\n" +
"Canonicalヘッダ\n" +
"HTTPリクエストのBody";
signature = hmac(digest(string_to_sign));
Apexのサンプルは以下のとおり
//Signatureの生成
private static String createSignature(Map<String, String> header_map, String postBody) {
String string_to_sign = '';
string_to_sign += 'POST' + Constants.LF;
string_to_sign += '/' + Constants.LF;
string_to_sign += '' + Constants.LF;
string_to_sign += createCanonicalHeaders(header_map) + Constants.LF;
string_to_sign += postBody;
System.debug(string_to_sign);
Blob b_sig = Crypto.generateMac(
'hmacSHA256',
Crypto.generateDigest(
'SHA256',
Blob.valueOf(string_to_sign)
),
Blob.valueOf(AWSAccessKeySecret)
);
return EncodingUtil.base64Encode(b_sig);
}
//Canonicalヘッダの生成
private static String createCanonicalHeaders(Map<String, String> headers) {
if (headers == null || headers.keySet().isEmpty()) {
return '';
}
Map<String, String> lowerCaseHeaders = new Map<String, String>();
for (String key : headers.keySet()) {
lowerCaseHeaders.put(key.toLowerCase(), headers.get(key));
}
String param = '';
List<String> sortedKey = getSortedList(lowerCaseHeaders.keySet());
for(String key: sortedKey){
param += key + ':' + lowerCaseHeaders.get(key).trim() + '\n';
}
return param;
}
SWFだとHTTPメソッドはPOST固定で
リソースパスも”/“固定、クエリパラメータはないので空白
ということでstring_to_signの生成は4行目からが動的な部分になります。
実際に生成したstring_to_signはこんな感じになります。
POST
/
host:swf.us-east-1.amazonaws.com
x-amz-date:Sat, 15 Feb 2014 17:04:24 +0900
x-amz-target:com.amazonaws.swf.service.model.SimpleWorkflowService.StartWorkflowExecution
{JSON Body...}
あとは生成したstring_to_signのdigest値からハッシュ値を計算してBase64エンコードするだけ。
アルゴリズムはSHA1 or SHA256で。
Signatureを生成したら、AWS認証用ヘッダに入れてコールアウト。
private static String callSWFAPI (String action, String postBody) {
Map<String, String> header_map = new Map<String, String> {
'Host' => SWF_ENDPOINT_HOST,
'X-Amz-Target' => 'com.amazonaws.swf.service.model.SimpleWorkflowService.' + action,
'X-Amz-Date' => DateTime.now().format('EEE, dd MMM yyyy HH:mm:ss ') + '+0900'
};
HttpRequest req = new HttpRequest();
for (String key : header_map.keySet()) {
req.setHeader(key, header_map.get(key));
}
req.setHeader(
'X-Amzn-Authorization',
'AWS3 AWSAccessKeyId=' + AWSAccessKeyId + ',' +
'Algorithm=HmacSHA256,' +
'Signature=' + createSignature(header_map, postBody)
);
req.setHeader('Content-Type', 'application/x-amz-json-1.0');
req.setHeader('Content-Length', String.valueOf(postBody.length()));
req.setEndpoint('https://' + SWF_ENDPOINT_HOST + '/');
req.setMethod('POST');
req.setBody(postBody);
Http http = new Http();
HTTPResponse res = http.send(req);
return res.getBody();
}
ちなみに、Host、X-Amz-Date、X-Amz-Targetヘッダは必須で
X-Amz-TargetはコールするAPIのメソッドを指定することになります。
Content-Typeもapplication/jsonとかじゃ通らないので、”application/x-amz-json-1.0”を指定。
HostはRESTのエンドポイントなのでリージョン毎の値を利用します。
あとは各メソッドに応じてX-Amz-TargetとHTTP Request Bodyを作っていくだけ!
ということで次回は実際にワークフローのスタート(StartWorkflowExecution)から
DeciderによるActivity Taskのスケジュール(ScheduleActivityTaskDecision)までをやってみます。