順次、選択、そして繰り返し

プログラミングのことやITのこと、たまに演劇のことも書きます。

Bluemix 大勉強会 - The Developer Focus - に参加してきた

6/21(水)開催の「Bluemix 大勉強会 - The Developer Focus -」に
参加してきました。

The Developer Focusという名の通り、開発者目線でのセッションが多かったです。

内容のレベルはBluemix初心者~中級者と書かれていたので
Bluemixを一度も触ったことが無い私は丁度いいぐらいの内容だったと思う。
(クラウドサービスではAWSを触っているので一旦AWSのサービスでいうところの……ってな感じで変換しながら聞いてた。)

しかしサービス以外の設計思想の部分でブロックチェーンなどの話をされていたときは
理解が追い付かなかったと感じた。
(やっぱりブロックチェーンについて理解する必要があるな……)

タイトル 概要や所感
Bluemixのロードマップや見どころ Watson関連のサービスでは全てのAPI・学習済みWatsonがBluemix上で提供されるようになる。
IaaSサービスでは最新のGPUが最安値で使えるようになる。
今から始める方向け!Bluemixの概要とアプリの作り方 CloudFoundly→PasSサービス。
AWSでいうとBeanstalkみたいなもの。
Bluemixで提供されているサービス・APIを組み立ててアプリを作成するイメージ。
各サービスの接続情報は「バインド」という仕組みで持っている。
今から始める方向け!Bluemixの概要とアプリの作り方
Bluemix Top10 サービス解体新書 Bluemixでよく使われているサービスのTop10紹介。
インスタンス数では「SDK for Node.js」が1位。
実行環境ではNode.jsが人気みたい。
Bluemix Top10 サービス解体新書
基幹システムとクラウドのつなげ方 IBMi(オンプレ)とBluemix(クラウド)をどう繋げるかの話。
DataConnectによるデータを統合する話。
20分でキャッチアップするBlockchain ~Hyperledger Fabric最新動向 Hyperledger FabricはThe Linux Foundationによってオープンソース化されたブロックチェーン基盤。
始めるにはDockerのイメージかBluemixのサービスからできる。
開発ツールとしてHyperledger Composerがある。JavaScriptで開発が可能。
DSX(Data Science Experience)ではじめるデータサイエンス DSXはBluemix上で提供されているデータ・サイエンス系分析の開発・実行環境のサービス。
データの取得先としてAWSサービスからも可能。
サーバーレス・アプリ開発のはじめ方 OpenWhiskの話。The Linux Foundationによってオープンソース化されている。
言語はNode.js・Python・Swift・Java・Docker(その他言語)。
Bluemix内外のサービスとの連携が簡単(SlackやGitHubなどのサービス連携が標準である。)
関数(アクション)をつなげる(シーケンス)ことも可能。
Watson IoT Platform全貌解説 Watsonと名前がついているがAIの機能は無い。
センサーデータの蓄積や可視化は有意義。
AIを使う前段階にWatson IoT Platformを使用するイメージ。
Watson IoT Platform全貌解説
Deep Security on Bluemix IaaSによるセキュリティ対策について クラウドではサーバの要塞化(多層防御)が必要。
サーバのインスタンス起動時にウイルス対策のソフトインストールするなどが必要。
バインドについて考えてみた CloudFoundlyのバインドについての話。
BluemixでのAPI公開の最初の一歩 APIConnectでAPIの定義をすることで
ポータル(APIのアクセス制限について)・マニュアルを自動で作成してくれる。
CloudFoundlyのAPIGatewayはBluemix内部で使う場合に有効。CloudFoundly同士をつなげるイメージ。
Node-REDのロードマップや見どころ GUIでノード(処理)をつなげて作成する(ASTERIA WARPみたいな感じ?)。PC・タブレットで操作可能。JavaScriptで複雑な処理を書くことが可能。
The Linux Foundationによってオープンソース化されている。
提供ノード数は1000近くある。JSONファイルからフローを読み込ませることも可能。
Watson Visual Recognition Tool で カンタン画像認識 大トロ・中トロ・赤身・サーモンの画像(教師データ)を用意して学習させるデモがあった。
簡単に画像認識のクラスを作れる。
Watson visual recognitionで簡単画像認識

Jenkinsビルド結果メールの設定について(特にSMTPサーバ)

Jenkinsでビルド結果を通知するためにメールの設定をするのだが、
SMTPサーバの設定について少し悩んだ。

初めてJenkinsをインストールした時は社内だったので社内のSMTPサーバを
設定すればよかったのだが、個人でJenkinsをインストールしようとした時に
SMTPサーバを持っているわけがないのでどうしようかと考えた。

調べてみるとGmailSMTPサーバが使えることが判明した。

メール通知の設定値

Jenkinsで「Jenkinsの管理」→「システムの設定」→「E-mail 通知」を開く

項目 設定値
SMTPサーバー smtp.gmail.com
E-mailのサフィックス 特に入力なし
SMTP認証 チェックを入れる
ユーザ名 Gmailのアカウント(xxx@gmail.com)
パスワード Gmailのパスワード
SSL チェックを入れる
SMTPポート 465
返信先アドレス 特に入力なし
文字セット UTF-8

設定後に「メールを送信して設定を確認」にチェックを入れて、
メール送信のテストをする。
指定したアドレスにメールが届いていれば設定完了。

エラーが出る場合はGmailのセキュリティ設定を変更すること。
GmailでGmail以外のアドレスでGmailのSMTPが使えなくなってた件(実は使えた件)

今回、AWSのEC2にJenkinsをインストールしていたのでSMTPサーバも
自前でEC2で立てようかと思ったがGmailSMTPサーバが使えたので楽できた。

でも知識習得のために自前でSMTPサーバも立ててみたいなと思ったり……。

JJUG CCC 2017 Springに参加してきた

5月17日にJJUG CCC 2017 Springに参加してきました。
(大分前だけど...)

ようやく書く気が起きたので、参加してきたセッションについて
まとめてみる。

全部 Java で作っちゃえ!! Vaadin 8 による "オール Java" Web アプリ開発のしくみと実践

スライド
全部 Java で作っちゃえ!😁 Vaadin 8 によるオール Java Web アプリ開発の仕組みと実践


AWTみたいなコード
https://demo.vaadin.com/sampler/
https://demo.vaadin.com/valo-theme/#!common

v7からv8への変更点
・データバインディング系のAPI

SpirngBootとの連携が進んでいる

Eclipseが一番成熟している
ビルドシステムは基本Maven

Vaadinを採用するモチベーション
・フロントとバックで型を共通化したい
・JSやHTML5の知識がある人材がチームにいなくても
 WebUIを作成したい
・俺はJava(JVM言語)が書きたい人

Javaエンジニアに知って欲しいRDBアンチパターン

スライド→
RDB anti-pattern that Java engineer wants to know // Speaker Deck

DBの変更は難しいので設計をきちんとする必要がある
・削除フラグで設計書を確認しないとわからない値が入っているなど
・カラムの追加 memo,memo2,memo3 とか
・データにロジックを埋め込むのはNG 先頭1桁がユーザの種類を表す会員IDなど

RDBの機能に頼りすぎるとそれが制約となる(ストアドなど)
→DBとアプリの設計のバランスが重要

Javaの話ではなくRDBの話ではあったが、
どのシステムでもDBは使用するため為になる内容だった。
DBAよりの話のためアプリ側の人は特に見た方が良いスライドだと思う。

Introduction of Project Jigsaw

スライド→
Introduction of Project Jigsaw


JSR376→Project Jigsawに関する草案
JSR379→SE9に関する草案

SE9(JSR379)は通過したがJigsaw(JSR376)が厳しい
ちなみにJSR379の中にJigsawの記述があるみたい

jar地獄
→依存関係の解消が面倒(バージョン違いで動かないことも)
Java言語として依存関係の表現ができない
mavenでは表現できるよね

上記を解決するためmoduleが登場した

module-info.java
→これがあればモジュールとする
依存性と公開範囲を記載する
階層のトップレベルに配置する
requiresで指定する(モジュール依存関係)
exportsは公開範囲

java —list-modules
→モジュール一覧

Jigsawの草案が否決されているから
SE9のリリースはまだ先になりそうだな...

新卒2年目から始めるOSSのススメ 〜明日からできるコミットデビュー〜

スライド→
新卒2年目から始めるOSSのススメ~明日からできるコミットデビュー~


コミットしたOSS→ERFlute
ER図を作成するツール

OSSの勧め
簡単な修正から始めよう
nullぽとか文字化けをなど簡単なところから始めよう

業務知識が関係ないのが良い
ソースコードが全て

特殊な環境だったためコミットできた
→メインコミッターが会社の人
→導入が事前に決定しているプラグインだった
→明確に不具合がわかっていた

このセッションを聞いて、OSSに対する
ハードルが低くなった気がした。
コミットまではいかなくてもまずはソースコード
読むところから始めてみようと思った。

AWS Amazon Rekognitionを使ってみた

Amazon RekognitionはAWSが提供しているサービスの1つで
深層学習に基づく画像分析を行ってくれるサービス。

このサービスを用いることで元の画像と比較対象の画像を
照らし合わせて同一人物がいるか探すことも可能である。

今回は複数の画像を用いた顔比較(同一人物かどうかを判定)を
試してみた。

大まかな流れは以下の感じ

①S3のバケットに事前に画像を格納する
②S3のバケットから画像を取得し、Collectionに格納する
③S3のバケットから1枚、比較対象の画像を取得して比較する
④顔比較の結果と画像を表示する

まず①の準備。
S3のバケットに適当な画像を格納しておく。
f:id:sndstudy:20170226224823p:plain

次は②
「rekognition.createCollection」でCollectionを作成する。
パラメータとして、「CollectionId(コレクションを識別するID)」が必要
Class: AWS.Rekognition — AWS SDK for JavaScript

「rekognition.indexFaces」でCollectionに画像を追加する。
Class: AWS.Rekognition — AWS SDK for JavaScript

普通に「rekognition.indexFaces」をループさせて画像を追加しようとしたが、非同期処理のため
Collectionに全ての画像が追加される前に次の処理に行ってしまう問題があったため
JavaScriptの「yield」を用いて処理を制御した。(コールバック地獄回避のため)
yield - JavaScript リダイレクト 1 | MDN

/********************************************/
//Rekognitionテスト処理
/********************************************/
var addFunction;

function rekognitionTest() {

    //Collection作成
    var paramsCreate = {
        CollectionId: "photos"
    };

    rekognition.createCollection(paramsCreate, function (err, data) {
        if (err) {
            console.log(err, err.stack);
        } else {
            addFunction = addImage();
            addFunction.next();
        }
    });

}

/********************************/
//Collectionに画像を追加する処理
/********************************/
function* addImage() {

    for (var cnt = 2; cnt <= 6; cnt++) {
        var paramsAdd = {
            CollectionId: "photos",
            Image: {
                S3Object: {
                    Bucket: "storage-keya-us",
                    Name: 'yone_san_' + cnt + '.jpg'
                }
            },
            ExternalImageId: 'yone_san_' + cnt + '.jpg'
        };

        rekognition.indexFaces(paramsAdd, function (err, data) {
            if (err) {
                console.log(err, err.stack);
            } else {
                console.log(data);
                addFunction.next();
            }
        });

        yield;
    }

    searchImg();
}

次は③
「rekognition.searchFacesByImage」で顔検索を行う。
Class: AWS.Rekognition — AWS SDK for JavaScript
「MaxFaces」は確か、似ている画像上位10枚までを表示するみたいな設定だったと思う。

/********************************/
//画像認識(元画像と似ている画像を探す)
/********************************/
var searchImg = function () {
    var params = {
        CollectionId: 'photos',

        Image: {
            S3Object: {
                Bucket: 'storage-keya-us',
                Name: 'yone_san_1.jpg'
            }
        },
        FaceMatchThreshold: 0.0,
        MaxFaces: 10
    };
    rekognition.searchFacesByImage(params, function (err, data) {
        if (err) {
            console.log(err, err.stack);
        } else {
            console.log(data);
            showImgs(data);
            deleteCollectiom();

        }
    });
}

最後は④
ここはS3から画像のURL取得と
画像の似ている度合いを表示する処理

/********************************/
//画像表示処理(画像認識結果)
/********************************/
function showImgs(data) {
    var s3 = new AWS.S3({
        params: {
            Bucket: 'storage-keya-us',
            Region: s3RegionName
        }
    });
    var htmlImg ="";
    for (var i = 0; i < data.FaceMatches.length;i++) {
        //URL取得
        var paramsSource = {
            Bucket: 'storage-keya-us',
            Key: data.FaceMatches[i].Face.ExternalImageId
        };

        var sourceUrl = s3.getSignedUrl('getObject', paramsSource);

        htmlImg += "<img src='" + sourceUrl + "'/><br/>" + "<span>" + "Similarity:" + data.FaceMatches[i].Similarity + "%" + "</span><br/>";
    }

    $("#imgList2").html(htmlImg);
    
    $('img').css('width','150px');
}

全体のソースコードはこんな感じ

$("#rekognitionButton").on("click", rekognitionTest);

var s3BucketName = 'translator-app-bucket';
var s3RegionName = 'us-east-1';

AWS.config.update({
    accessKeyId: 'your_accessKeyId',
    secretAccessKey: 'your_secretAccessKey'
});

var rekognition = new AWS.Rekognition({
    region: 'us-east-1'
});

/********************************************/
//Rekognitionテスト処理
/********************************************/
var addFunction;

function rekognitionTest() {

    //Collection作成
    var paramsCreate = {
        CollectionId: "photos"
    };

    rekognition.createCollection(paramsCreate, function (err, data) {
        if (err) {
            console.log(err, err.stack);
        } else {
            addFunction = addImage();
            addFunction.next();
        }
    });

}

/********************************/
//Collectionに画像を追加する処理
/********************************/
function* addImage() {

    for (var cnt = 2; cnt <= 6; cnt++) {
        var paramsAdd = {
            CollectionId: "photos",
            Image: {
                S3Object: {
                    Bucket: "storage-keya-us",
                    Name: 'yone_san_' + cnt + '.jpg'
                }
            },
            ExternalImageId: 'yone_san_' + cnt + '.jpg'
        };

        rekognition.indexFaces(paramsAdd, function (err, data) {
            if (err) {
                console.log(err, err.stack);
            } else {
                console.log(data);
                addFunction.next();
            }
        });

        yield;
    }

    searchImg();
}

/********************************/
//Collection削除処理
/********************************/
var deleteCollectiom = function () {
    var paramsDelete = {
        CollectionId: "photos"
    };
    rekognition.deleteCollection(paramsDelete, function (err, data) {
        if (err) {
            console.log(err, err.stack);
        } else {
            console.log(data);
        }
    });
}

/********************************/
//画像認識(元画像と似ている画像を探す)
/********************************/
var searchImg = function () {
    var params = {
        CollectionId: 'photos',

        Image: {
            S3Object: {
                Bucket: 'storage-keya-us',
                Name: 'yone_san_1.jpg'
            }
        },
        FaceMatchThreshold: 0.0,
        MaxFaces: 10
    };
    rekognition.searchFacesByImage(params, function (err, data) {
        if (err) {
            console.log(err, err.stack);
        } else {
            console.log(data);
            showImgs(data);
            deleteCollectiom();

        }
    });
}

/********************************/
//画像表示処理(画像認識結果)
/********************************/
function showImgs(data) {
    var s3 = new AWS.S3({
        params: {
            Bucket: 'storage-keya-us',
            Region: s3RegionName
        }
    });
    var htmlImg ="";
    for (var i = 0; i < data.FaceMatches.length;i++) {
        //URL取得
        var paramsSource = {
            Bucket: 'storage-keya-us',
            Key: data.FaceMatches[i].Face.ExternalImageId
        };

        var sourceUrl = s3.getSignedUrl('getObject', paramsSource);

        htmlImg += "<img src='" + sourceUrl + "'/><br/>" + "<span>" + "Similarity:" + data.FaceMatches[i].Similarity + "%" + "</span><br/>";
    }

    $("#imgList2").html(htmlImg);
    
    $('img').css('width','150px');
}

htmlファイルはこんな感じ

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>Test</title>

    <!-- jQueryCDN -->
    <script src="https://code.jquery.com/jquery-3.1.1.js"></script>

    <!-- AWS SDK for JavaScript -->
    <script src="https://sdk.amazonaws.com/js/aws-sdk-2.7.27.min.js"></script>
    <script type="text/javascript" src="./js/main.js"></script>

</head>

<body>

    <h3>Rekognitionテスト</h3>
    <img src="https://s3.amazonaws.com/storage-keya-us/yone_san_1.jpg" style="width:150px"/>
    <br>
    <input type="button" value="searchFaces" id="rekognitionButton">

    <div id="imgList2"></div>

    <script type="text/javascript" src="./js/main.js"></script>

</body>

</html>


表示画面はこんな感じ
f:id:sndstudy:20170226231050p:plain

ボタンを押下すると画像と似ている度合いを表示してくれる
f:id:sndstudy:20170226231211p:plain

※一応同一人物だけどSimilarity(信頼度)にもの凄い差がある……。
 ちなみに二人セゾン時のアー写もS3に格納したはずが、
 似てなさ過ぎて表示されないらしい。
 まぁ成長したと捉えればいいのか。

今回はブラウザのJavaScriptで実装してみたけど、意外と簡単に
実装することができた。
これぐらい簡単なら画像認識系の機能は自分で実装せず、
クラウドサービスに任せるのもアリかもしれない。

他の値とかを取得したければ、自分で画像認識の機能を
実装するしかないんだけどね……。

■参考サイト
Class: AWS.Rekognition — AWS SDK for JavaScript
yield - JavaScript リダイレクト 1 | MDN
米谷奈々未 - エケペディア
→画像を拝借

AWSIoT httpを用いてのpublish(メモ)

console.log('start');

$("#button").on("click", test);

var iotdata = new AWS.IotData({
    endpoint: 'endpoint',
    accessKeyId: 'accessKeyId',
    secretAccessKey: 'secretAccessKey',
    region: 'region'
});

function test() {

    console.log('click');

    var params = {
        topic: 'testTopic',
        payload: JSON.stringify({
            message: "hello"
        }),
        qos: 1
    };


    //メッセージをpublishする
    iotdata.publish(params, function (err, data) {
        if (err) {
            console.log(err, err.stack);
        } else {
            console.log(data);
        }
    });

}

APIGatewayからAWSLambdaを叩く(POST送信)

『サーバーレスアーキテクチャ』という言葉にひかれて、
APIGatewayからAWSLambdaを叩いてみた。
そして、色々詰まったのでメモとして残しておく。


まずはAPIGatewayの設定。

メソッドリクエストの設定。
f:id:sndstudy:20170118005347p:plain

リクエスト本文のコンテンツタイプに
application/x-www-form-urlencoded を設定する。
これを設定することFormの値をPOSTメソッドでも送信することができる。

統合リクエストの設定。
f:id:sndstudy:20170118005833p:plain
f:id:sndstudy:20170118005856p:plain

本文マッピングテンプレートのコンテンツタイプは
メソッドリクエストと合わせる。
また、ここでリクエストボディに詰められた
Formの値を取り出すためにマッピングテンプレートを設定する。

マッピングテンプレートはVTLで記述する。
Velocityの文法 - Javaについて

{
    "headers" : {
        #foreach( $key in $input.params().header.keySet() )
            "$key" : "$input.params().header.get($key)"#if( $foreach.hasNext ),#end
        #end
    },
    "queryParameters" : {
        #set( $tmpstr = $input.body )
        #foreach( $keyandvaluestr in $tmpstr.split( '&' ) )
        #set( $keyandvaluearray = $keyandvaluestr.split( '=' ) )
            "$keyandvaluearray[0]" : "$keyandvaluearray[1]"#if( $foreach.hasNext ),#end
        #end
    },
    "stage" : "$context.stage",
    "sourceIp" : "$context.identity.sourceIp",
    "userAgent" : "$context.identity.userAgent"
}

特にこの部分でFormで送信したkeyとvalueを取り出している。

    "queryParameters" : {
        #set( $tmpstr = $input.body )
        #foreach( $keyandvaluestr in $tmpstr.split( '&' ) )
        #set( $keyandvaluearray = $keyandvaluestr.split( '=' ) )
            "$keyandvaluearray[0]" : "$keyandvaluearray[1]"#if( $foreach.hasNext ),#end
        #end

統合レスポンスの設定。
今回はFormで2つの値を送信し、足し算を行った結果を
htmlで表示するためコンテンツタイプを
text/htmlに設定する。

f:id:sndstudy:20170118010532p:plain

本文テンプレートは以下のように、計算結果を表示するように記述。

<html>
    <head>
        <title>title</title>    
    </head>
    <body>
        <span>$input.path('ans')</span>
    </body>
    
</html>


メソッドレスポンスでは、
統合レスポンスと同様にtext/htmlを設定する。
f:id:sndstudy:20170118010941p:plain

次はLambdaの作成。
今回はNode.jsで作成。

f:id:sndstudy:20170118011146p:plain

exports.handler = (event, context, callback) => {
    
    //値を取得する
    var num1 = parseInt(event.queryParameters.param1);
    var num2 = parseInt(event.queryParameters.param2);
    var answer = num1 + num2;
    
    //レスポンス
    context.done(null, {"ans": answer});
    
};

最後はFormを送信するhtmlを作成。

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>LambdaTest</title>

</head>
<body>
    
    <form action="create api url" method="post">
        
        値1:<input type="text" name="param1"><br>
        値2:<input type="text" name="param2">
        
        <input type="submit" name="test">
    </form>

</body>
</html>

実行結果はこんな感じ。
f:id:sndstudy:20170118011543p:plain
f:id:sndstudy:20170118011701p:plain

特に詰まったのは統合リクエストのマッピングテンプレートだった。
VLTは初めてだったので、処理を書くのに時間がかかった。

あと、今回APIを作成したおかげでGETとPOSTのリクエストの中身を
細かく知ることができてよかった。(POSTはリクエストボディがあるなど)


■参考サイト
API Gateway + Lambda にFormからPOSTする時のマッピングテンプレートを作成しました | Developers.IO
[Web] HTTPリクエストの中身を学んでみた。GETやPOSTの違いなど - YoheiM .NET
HTTP入門
Amazon API Gatewayをブラウザから呼んでみた | Developers.IO
パーセントエンコーディング - Wikipedia
application/x-www-form-urlencoded ‐ 通信用語の基礎知識
node.jsのcontext.succeedで気をつけること | hacknote
docs.aws.amazon.com