まめログ

Javaプログラマの歩み

Spring Boot + Thymeleafでバリデーションを実装する

バリデーションとは

Webアプリにおいて、ユーザが入力した値や送信されてきたデータが適切かどうかを判定する処理のこと。
適切なバリデーションを実装することで、システムにおける不正データでの流入を防ぐとともに、ユーザエクスペリエンスも向上させることができる。

主なバリデーション

バリデーションの内容には以下のようなものがある。

必須項目

必ず入力されていなければならない値のチェック

文字列の長さ

最小・最大長

データ型

数字、日付など

フォーマット検証

電話番号やメールアドレスなど

値の範囲

0~100など

Spring Bootとバリデーション

Spring BootではBean Validationという便利なバリデーション機能を提供しており、バリデーション定義をアノテーションベースで簡単に実装できる。
サーバサイドのエンティティクラスにアノテーションを付与することで、フロントエンドでのバリデーションが行える。

Bean Validationの導入

pom.xmlに以下のdependencyを追加する。

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
			<version>3.5.3</version>
		</dependency>

よく使うアノテーション

@NotNull 必須チェック(NULLの場合NG)
@NotEmpty|必須チェック(NULL、空文字の場合NG)|
@Size 桁数チェック
@Min 数値の最小値チェック
@Max 数値の最大値チェック
@Email メールアドレスの書式

エンティティクラスにバリデーションを追加

バリデーションをつけたいフィールド変数の宣言の上にバリデーション用のアノテーションを付与することで、バリデーション定義を実装することができる。

    @NotEmpty
    private String artistName;

コントローラにバリデーション処理を組み込む

コントローラでバリデーション処理を実行するには、バリデーションを行いたいエンティティクラスの引数の前に@Validatedアノテーションを付与する。

public String registerArtist(@ModelAttribute @Validated Artist artist, @RequestParam("artist_cover") MultipartFile cover,Model model) {

}

バリデーション結果を格納する変数を用意する

コントローラで行われたバリデーション結果を格納するため、BindingResult型の変数をコントローラの該当メソッドの引数に追加する。
この際、バリデーション対象の引数の次の引数として宣言する必要があるので注意。

public String registerArtist(@ModelAttribute @Validated Artist artist, BindingResult result, @RequestParam("artist_cover") MultipartFile cover,Model model) {
}

バリデーション結果を判定する

バリデーションの結果のエラーの有無ははBidingResultのhasErrors()メソッドで取得することができる。
バリデーションエラーがあった際に、画面遷移先が異なる場合などには判定処理をいれる必要がある。

        if(result.hasErrors()){
            return "/artist/register"; //エラー時の遷移先
        }

バリデーション結果を画面に表示する

エラー判定処理とエラー内容のタグを加える。
htmlには以下のようにタグを追加すれば、エラー発生時にエラー内容を表示することができる。

<div class="fw-bold" th:if="${#fields.hasErrors('artistName')}" th:errors="*{artistName}" th:errorclass="err" style="color:red"></div>

Controllerでの判定処理は引数にBindingResult型の変数を追加することで行える。

    @PostMapping("/artists")
    public String registerArtist(@ModelAttribute @Validated Artist artist, BindingResult result, @RequestParam("artist_cover") MultipartFile cover,Model model) {
        if(result.hasErrors()){
            return "/artist/register";
        }
        artistService.registerArtist(artist,cover);
        return "redirect:/artists";
    }

Spring Boot + Thymeleafで画面のあるWebアプリケーションを開発する

Spring Boot + Thymeleafで画面のあるWebアプリケーションを開発する

MVCの基礎

Thymeleafとは

ThymeleafはSpringと連携機能が提供されているテンプレートエンジンである。
HTML文法に則ったテンプレートを作成でき、作成したテンプレートファイルはそのままブラウザで開く*ことができる。
テンプレートのHTMLタグ内にThymeleafのタグを埋め込むことで動的要素を構成することができる。

<html>
 <body>
  <h1 th:text="${fullName}">電子たろう</h1>
 </body>
</html>

*直接ブラウザで開いた際にはTypmeleafの属性は無視される

PJへThymeleafを追加する

pom.xmlの修正

新規PJの場合は、Spring InitializrでDepedencyにThymeleafを追加するだけで使用可能になる。

既存PJの場合は、Mavenを使っている場合にはpom.xmlのdependenciesに以下の記述を追加する。

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>

また開発時に便利な機能を提供してくれる以下の設定も入れておくと良い。
これを入れておくと、ソースコード修正時に自動的にAPサーバーが再起動され、修正した内容がすくに反映されるといった機能が利用できるようになる。

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
        <version>3.5.0</version>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>

Thymeleafを動かす

Controllerクラスの作成

Viewを伴うCotrollerクラスの場合は、@Controllerアノテーションを付与して宣言する。
リクエスに対応する処理はメソッドで記述する。メソッドの戻り値はView名にする。
引数のmodelに対して属性を追加することで、Viewに値を渡すことができる。

@Controller
public class SampleController {

    @GetMapping("/greeting")
    public String greeting(Model model){
        model.addAttribute("fullName", "Hello 電子じろう!!");
        return "greeting";
    }
}
Viewの作成

Viewはテンプレートファイルとして作成する。テンプレートファイルはsrc/main/resources配下の「templates」フォルダに配置する。
Controllerの戻り値の文字列とファイル名を一致させておく。

<html>
 <body>
  <h1 th:text="${fullName}">電子たろう</h1>
 </body>
</html>
 
実行して確認

サーバサイドで設定した値が表示されることを確認できた。

Spring Boot + Spring Data JPA+ ThymeleeafでDBアクセスを実装

DBにアクセスを伴うWebアプリケーションの開発

Spring Initializrで雛形を作成する

Dependenciesは以下のようにする。利用するRDBMSが違う場合は対応するDriverに変更すること。

application.propertiesにDB接続情報を記述する

以下はH2 DBに接続するための接続情報である。SQL Serverや他の異なるRDBMSを利用する場合は適宜変更すること。

spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.sql.init.encoding=UTF-8
spring.sql.init.data-locations=classpath:data.sql
spring.datasource.initialize=true

パッケージの作成

src/main/java/<アプリケーションのパス>の配下に「entity」、「service」、「controller」、「repository」のパッケージを作成する。

Entityクラスの作成

今回は歌手(グループ)を表すクラスを作成し、DBで管理を行うアプリをサンプルとして作成する。
そのために、EntityクラスとしてArtistクラスを作成する。

@Entity
@Table(name = "artists")
@Data
public class Artist {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer artistId;

    private String artistName;

    private String artistHiraganaName;

    private String artistArtUrl;
}

Repositoryインタフェースの作成

Artistのレコードをやり取りするためのRepositoryインタフェースを作成する。

@Repository
public interface ArtistRepository extends CrudRepository<Artist, Integer> {

    public List<Artist> findAll();
}

Serviceクラスの作成

@Service
public class ArtistService {

    private final ArtistRepository artistRepository;

    @Autowired
    public ArtistService(ArtistRepository artistRepository) {
        this.artistRepository = artistRepository;
    }

    /**
     * 全件検索
     * @return テーブル内にある全レコード
     */
    public List<Artist> findAll() {
        return artistRepository.findAll();
    }
}

Controllerクラスの作成

Controllerクラスではリクエストパスの設定と、遷移先のHTMLファイル名を指定する。
値をサーバーサイドから受け取って、Viewに渡す処理もここで行う。

@Controller
public class ArtistController {

    private final ArtistService artistService;

    @Autowired
    public ArtistController(ArtistService artistService) {
        this.artistService = artistService;
    }

    @GetMapping("/artists")
    public String findAll(Model model) {
        var artists = artistService.findAll();

        model.addAttribute("artists", artists);
        return "artists";
    }

Viewの作成

全アーティスの情報を表示するためのartists.htmlファイルを作成する。
Thymleafで繰り返し処理を行うには、th:eachタグを利用する。
構文は th:each="要素変数 : ${リスト}"となっており、拡張for文に似ている。
要素変数の値には「要素変数.(ドット)」でアクセスできる*。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>アーティスト一覧</title>
</head>
<body>
<table border="1">
  <tr>
    <th>アーティスト名</th>
    <th>アーティスト名(読み)</th>
    <th>公式HP</th>
  </tr>
  <tr th:each="artist: ${artists}">
    <td th:text="${artist.artistName}"></td>
    <td th:text="${artist.artistHiraganaName}"></td>
    <td th:text="${artist.artistArtUrl}"></td>
  </tr>
</table>
</body>
</html>

*メソッドも実行できる。

動作確認

Spring Bootを実行し、Webブラウザでアクセスすれば動作が確認できる。

【Java】Listから条件に一致した要素だけを抽出する方法(ArrayList, Stream API)

Listに格納された要素の中からある条件に一致した要素だけを抽出したい場面がよくあります。
その際にはfor文とif文を使う方法、Stream APIのfilterメソッドを使う方法などがあります。

for文を使って条件に一致した要素を抽出する

String型のListから要素を抽出する方法を見てみます。

 //コレクション変数の宣言と初期化
 List<String> names = new ArrayList<>();
 //要素の追加
 names.add("白石麻衣");
 names.add("生田絵梨花");
 names.add("松村沙友理");

コレクションから条件に一致する要素を抽出するには、
コレクションの要素すべてを条件と比較し、条件に一致したもののみを格納する新たなコレクションを作ることで実現できます。

//結果格納用のコレクションの宣言
List<String> matched = new ArrayList<>();
//for文でコレクション内の要素すべてを条件と比較する
for (int i = 0; i < names.size(); i++) {
    //"白"で始まる要素かどうかを判定する
    if(names.get(i).startsWith("白")){
        //条件に一致した場合、結果格納用のコレクションに要素を追加する
        matched.add(names.get(i));
    }
}
//抽出された項目をコンソールに出力
for(String name : matched){
    System.out.println("条件一致 : " + name);
}

Stream APIを使って条件に一致した項目を抽出する

Stream APIを使うと、先程の抽出処理を簡単に記述することが出来ます。
抽出対象のコレクションは先程と同じものを使います。

 //コレクション変数の宣言と初期化
 List<String> names = new ArrayList<>();
 //要素の追加
 names.add("白石麻衣");
 names.add("生田絵梨花");
 names.add("松村沙友理");
//filterメソッドで抽出する条件を記述する
List<String> matched = names.stream().filter(n -> n.startsWith("白")).toList();

また、抽出した結果をそのままコンソールに出力するだけであれば、
格納用の変数を用意することなく、forEachメソッドをつなげることで一行で完結させることが出来ます。

names.stream().filter(n -> n.startsWith("白")).forEach(n -> System.out.println("条件一致 : " + n));

【Java】ListインタフェースとArrayListの使い方 入門

Javaのコレクション Listインタフェース

Javaには複数のデータを扱うための仕組みが用意されており、その中の一つがListです。
今回はListの使い方を解説します。
docs.oracle.com

Listの宣言と初期化

List自体はインタフェースであり、実装クラスのインスタンスを生成する必要があります。
Listを実装したクラスとしては、ArrayListとLinkedListがあります。
今回はArrayListを使ってコードを記述していきます。
まず宣言です。

List<String> names = new ArrayList<>();

実装クラスのArrayList型ではなくListインタフェース型として変数を宣言します。*1
<>(ダイヤモンド演算子)の中に、コレクションに格納したい型を記述することで、
その型専用のコレクションとして使うことができます。

//Student型のコレクションを宣言・初期化
List<Student> students = new ArrayList<>();
//String型のコレクションを宣言・初期化
List<String> names = new ArrayList<>();

変数名はコレクションなので複数形にすることが多いです。
newするクラスはArrayListで、こちらにもダイヤモンド演算子がありますが、こちらの型の記述は省略することができます。
docs.oracle.com

要素の追加

Listに要素を追加するにはaddメソッドを用います。

names.add("白石麻衣");
names.add("生田絵梨花");
names.add("松村沙友理");

配列と違い、要素数は可変なので追加する際に気にする必要はありません。

要素の挿入

要素の挿入にもaddメソッドを使います。追加と違うのは第1引数に挿入するインデックス番号を指定することです。

//インデックス1に要素が挿入される。インデックス1以降にあった要素は、インデックス2以降に自動的に移動させられる。
names.add(1,"橋本奈々未");

要素の削除

Listの要素を削除するにはremoveメソッドを用います。
removeメソッドの引数には削除したい要素のインデックス番号を用います。コレクションも配列同様インデックスは0から始まります。
また、removeメソッドの戻り値は削除した要素です。

names.remove(2) 

要素の取得

List内に格納された要素を取得するにはgetメソッドを用います。
getメソッドの引数には取得したい要素のインデックス番号を指定します。コレクションも配列同様インデックスは0から始まります。

System.out.println(names.get(0)); //白石麻衣と表示される

素数の取得

Listに格納されている要素数を取得するにはsizeメソッドを用います。

names.size();

Stringを格納するListの変数宣言から要素の追加・削除・要素の取得の一連の流れ

ここまでで説明したListの宣言方法とその要素の追加や削除など一連の流れをまとめて説明します。

//コレクション変数の宣言と初期化
List<String> names = new ArrayList<>();
//要素の追加
names.add("白石麻衣");
names.add("生田絵梨花");
names.add("松村沙友理");
   
 //要素の削除
 names.remove(1);
 //for文による全件操作
 for (int i = 0; i < names.size(); i++) {
      System.out.println(names);
 }

独自オブジェクトを格納するListの変数宣言から要素の追加・削除・要素の取得の一連の流れ

ダイヤモンド演算子で指定するクラスはJavaの標準のクラスだけではなく、自分で作成したクラスを指定することも出来ます。
今回はStudentクラスというクラスを作成し、そのコレクションを宣言、使用します。

//Student型のコレクションを宣言・初期化
List<Student> students = new ArrayList<>();
//Listに格納するStudent型インスタンスの作成
students.add(new Student("111","遥香","賀喜"));
students.add(new Student("222","さくら","遠藤"));
//メソッドの戻り値を直接格納することもできる
students.add(getStudentsByNumber("333"));

students.stream().forEach(System.out::println);

for文による全件操作

for文を使ってコレクションを操作する方法です。
素数は配列と違ってsizeメソッドを用い、要素の取得にはgetメソッドを用います。

for (int i = 0; i < names.size(); i++) {
            System.out.println(names.get(i));
}

拡張for文による全件操作

拡張for文で記述することも可能です。

for (String name : names) {
      System.out.println(name);
}

StreamとforEachを使った全件操作

Java8から導入されたStream APIを使っても操作することが可能です。
今現在はこの手法が主流だと思います。

names.stream().forEach(System.out::println);

*1:ArrayList型で宣言しても問題はない

2019年の振り返り:社会人学生としての8ヶ月と転職 #社会人学生

あけましておめでとうございます。

2019年は、個人的に2つの大きなイベントがありました。

転職と進学です。
ちょっと振り返りたいと思います。

転職

2019年4月末で4年弱務めたクレジットカードの決済代行会社を辞め、2019年5月に現職に転職しました。
前職・現職ともに金融業界に属する会社で、
そこのシステム部門で実際にコードを書いたり、業務部門と調整したり、外部ベンダーの成果物をレビューしたりと
業務内容に大きな違いはありません。
一つ大きな違いとしては、現職が外資系と言われる会社でメールやチャットの半分くらいが英語でやり取りされていることくらいです。
Google翻訳を駆使しながら必死に読み書きしてやり過ごしています。*1

転職は3回目で、今の会社で4社目です。

経歴

軽く自分の経歴を。
見ての通り、学歴も職歴もスキルもしょぼいです。

2009年3月 3年制の情報系専門学校を卒業
2009年4月 底辺SES企業に就職。オペレータ業務やシステム周りの雑用業務などが主な会社でした。
     いわゆるブラックで、退職時に有給消化を申し出ると恫喝されたりしました。
2013年3月 1回目の転職。パッケージ開発をしたり、2次受けのSI事業の会社でした。
     ここでプログラマーとしてのキャリアがスタートしています。Struts1,Struts2系での開発が主でした。
2015年7月 2回目の転職。クレジットカードの決済代行会社の内製エンジニアとして採用されました。
     SpringやJava EE7での開発が主でした。社内システムの一部はKotlinで作ったりもしました。
     ログ管理が出来ておらず、Elastic Stackを勝手に導入し効果を実際に体感してもらうことで、本格導入にこぎつけたりしました。
2019年4月 産業技術大学院大学 情報アーキテクチャ専攻 進学
2019年5月 3回目の転職。現在に至る。

転職活動について

前職については給与と業務内容*2に不満があったので、
ゆるーく転職活動をしていました。
活動期間は2018年11月~2019年2月です。

使用媒体について

媒体はLinkedInを使いました。
LinkedInでは人事部門からの直接のコンタクトや、外資系エージェントからのコンタクトが多くあり、興味のあるものにだけ返信してました。
それでも15社くらいに書類を送って10社弱面接まで行けました。

受けた企業群

受けた企業は

などを10社くらい受けて、いくつか内定をもらったので、そこから選んで今の会社に転職しました。
前職が金融系ということでか、金融系の会社からのお誘いを多くいただけました。
おそらく次に転職することがあるとすれば、また金融系になるような気がしています。*3

殆どの面接が英語だったので、英語がきちんと話せればもっとチャンスは広がったのになあという感想です。

進学

以前のエントリーにある通り、2019年4月から産業技術大学院大学(AIIT)の情報アーキテクチャ専攻に進学しました。
mamelog.hatenablog.jp

入学から8ヶ月経過し、4クォーター制の授業のうち3クォーターを修了しましたので、感想や分かったことを書いてみます。

学び直しは楽しい

そもそも学生時代に学んだ記憶がないのですが、30過ぎてから学び直すってのも楽しいものですね。
去年から社会人をやりながら学び直す社会人が増えてるように思います。
社会人が学べる環境がもっと整っていくと良いと思っています。

fushiroyama.hatenablog.com

soudai.hatenablog.com

二足の草鞋の生活について

平日の授業は6限(18:30~20:00)と7限(20:10~21:40)です。
職場の場所にもよりますが、6限の授業を履修していると授業開始に間に合うためには、定時退社が必須になります。
土曜は1限から5限(09:00~18:00)まであります。
人にもよりますが、1クォーターで2~5つの授業を履修し、週1日~週5日程度通学しています。

平日の授業がある日のスケジュールはこんな感じです。
09:00~17:30 仕事
17;30~18:30 移動
18:30~21:40 授業(2限続けての場合)
~22:45 図書館で自習(出席できなかった授業のビデオ録画を見たり、録画授業を見たり、レポート課題などを書いたりなど)
23:00~24:00 電車移動、帰宅
レポート課題がなかったり、疲れているときは図書館に寄らず帰ることもあります。

CS(Computer Science)とIS(Information Systems)は違う

産業技術大学院大学情報アーキテクチャ専攻では卒業すると、
「情報システム学修士(専門職)」という学位*4が貰えるのですが、
これはいわゆるComputer Scienceとは違うもののようです。

違いを検索してみると以下のようなページがヒットしました。
medium.com

Computer Scienceはアルゴリズムの研究などを行い、Information SystemsはITを駆使してビジネスを良くするもののようです。
CSがいわゆるGAFAでプラットフォームを作る人や研究職で、
ISはSIerや事業会社でシステム開発を行っていく人のイメージが近い*5ような気がします。
授業のカリキュラムも、コンピュータの基礎原理やプログラミングの授業もちろんありますが、経営・マーケティング・ビジネスアナリシスなどが充実しています。

ISを卒業すると、GAFAに行けないのかというとそうでもなく、
多くの求人では、"CSの学士、修士以上、もしくはそれに近い学科の学位"が求められているので、
自身で競技プログラミングなどで、アルゴリズム力を鍛えておけば十分に可能性はあると思います。

結局は自分自身です。

専門職大学院は研究より実務

大学院というと研究ばかりというイメージの人も多いと思いますが、
産業技術大学院大学専門職大学院という新しいくくりの学校で、研究よりは実務重視のカリキュラムとなっています。
その証拠に2年次は修士論文の執筆ではなく、PBL(Project Based Learning)というプロジェクトを数人で行うことで修学します。
プロジェクトは様々で、Webサービスを開発したり、産学連携で自治体と共同で検証したりなど様々です。
もちろん、プロジェクトの合間で論文を執筆し、学会で発表する人も少なからずいます。
修士論文の執筆の機会がないというわけではないです。*6

課題のボリューム

1週間に1科目に付き2コマ授業があり、レポート課題やコーディングの課題が出されます。
課題のボリュームはだいたい週に3~10時間程度の自習時間が求められる印象です。
関連論文や技術書まで読んでいくとさらに時間がかかるかと思いますが、私はそこまではやっていません。

交通費が結構掛かる

キャンパスがりんかい線品川シーサイド駅にあるため、週4日も通うと交通費が結構馬鹿になりません。
最初は通勤定期にしていましたが、通学定期に切り替えました。

入学したいと思ったら

2020年4月入学にはまだ間に合います。
aiit.ac.jp

さいごに

2019年の転職も進学も、人生の転機としてとてもいいものでした。
勝負は今年なので、ふるい落とされないように頑張っていこうと思います。

*1:会議は通訳が付きます

*2:四六時中社用ケータイを持たされ、電話に無償で出なくてはいけない

*3:特に金融に詳しいわけではないですが。

*4:英語名:Master of Technology in Information Systems

*5:ISであってもコードはバリバリ書く

*6:PBLで週18時間の稼働が求められるので、論文執筆は修羅場となりますが

ラズベリーパイに繋いだ温度・湿度センサー(DHT11)の情報をElasticsearch & Kibanaでリアルタイム可視化した

ラズベリーパイに繋いだ温度・湿度センサーの情報をElasticsearchに投入して、Kibanaで可視化してみたので、手順を残しておきます。
f:id:mamepika:20190730014501j:plain

Elasticsearch & KibanaはAmazon EC2にインストールして実行しています。
インストールなどの手順は以下をご参照ください。
mamelog.hatenablog.jp

ラズベリーパイについて

ラズベリーパイとセンサー類はAmazonで購入しました。
Amazon CAPTCHA

Amazon CAPTCHA

ラズベリーパイの準備

USBマウスとUSBキーボード、HDMI入力を受け付けるディスプレイが必要です。*1
OSに関しては画面の指示に従っていけば、特に問題なくインストールできると思います。
f:id:mamepika:20190730014944j:plain

温度・湿度センサー(DHT11)をラズベリーパイに接続する

以下の写真のように、センサーセットの中にあるケーブルを使って接続します。
f:id:mamepika:20190730015106j:plain

温度・湿度を取得し表示する

DHT11はGitHubに取得用のコードを公開してる人がいるので、それを使います。
github.com

ラズパイ上でgit cloneしましょう

$ git clone https://github.com/szazo/DHT11_Python

dht11_example.pyを実行すると、温度・湿度が取得できます。

Elasticsearchにデータを送信する

Elasticsearchにデータを送るために、コードを読み解きます。

サンプルコードについて

dht11_example.pyを見ると、以下のコードでセンサーデータを取得していることが分かります。

instance = dht11.DHT11(pin=14)
result = instance.read()

result.temperature
result.humidity

単純なコードを埋め込むだけで、温度・湿度が取れそうです。

Elasticsearchクライアントの導入

ElasticsearchのPythonクライアントをダウンロードします。*2

$ sudo pip3 install elasticsearch

Elasticsearchに外部からアクセス可能にする

Elasticsearchはデフォルトの設定では外部からアクセスが出来ないので、
外部からのアクセスを許可する設定を入れる必要があります。
具体的には/etc/elasticsearch/elasticsearch.ymlを編集します。

$ sudo vi /etc/elasticsearch/elasticsearch.yml

以下の設定を入れて保存します。
networkhost: "0.0.0.0"

Elasticsearch用のコードの編集

取り敢えず、コードを貼り付けちゃいます。
温度・湿度を取り敢えず可視化するだけならこれで可能です。

#!/usr/bin/env python3

import RPi.GPIO as GPIO
import dht11
import time
import datetime
import socket
from elasticsearch import Elasticsearch

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.cleanup()

es = Elasticsearch('<Elasticsearch IP Address>:9200')
instance = dht11.DHT11(pin=14)

while True:
    sensorData = instance.read()
    timestamp = datetime.datetime.now()
    if sensorData.is_valid():
        es_body = {
            '@timestamp' : timestamp.isoformat(),
            'temperture' : sensorData.temperature,
            'humidity' : sensorData.humidity
        }
        response = es.index(index='rspi',doc_type='doc',body=es_body)
    print(response)
    time.sleep(5)

このコードを適当なファイル名で保存して、dht11.pyと同じフォルダに置き、バックグラウンド実行しておけば5秒おきにセンサーデータが送られます。

Kibanaでこのようなグラフを作って、可視化することが出来ます。
f:id:mamepika:20190730022754j:plain

SSH接続やElasticsearchのインデックスの作成など、いくつか粗はありますが、取り敢えずの実践ということで。

Kibanaでの画面自動更新設定方法

V7から?かな今まで画面右上部にあった自動更新設定が見当たらず、
自動更新が出来てなかったんで調べたら、以下の画像のように、期間設定のところに融合していました。
f:id:mamepika:20190730023748p:plain

時差問題

タイムゾーン問題でこのコードだとKibanaでの表示が9時間ずれてしまいます。
解決策があれば教えていただきたく。

*1:ディスプレイはTVでも代用可能

*2:pip3の実行にはroot権限が必要のようでした

Amazon EC2にElasticsearch & Kibana 7.2をインストールし実行する

f:id:mamepika:20190729211607p:plain

授業の一環で、ElasticsearchとKibanaを使う機会が有ったので備忘録として書いておきます。
前職ではV6.2を使っており、その後1年以上アップデートせず使っていたので、V7.2のUIに少し戸惑いました。
本当はDockerコンテナ使ってECSでやってみたかったのだけど妥協してしまいました。

Amazon EC2インスタンスを起動する。

OSはAmazon Linuxを採用。
f:id:mamepika:20190729211849p:plain

メモリ調整をすれば、最小インスタンスでも動くようですが、面倒なのでここは
2コア、4GBメモリのt2.mediumインスタンスを使います。*1
f:id:mamepika:20190729212006p:plain

ストレージやセキュリティグループは適当に済ませ起動させましょう。

Javaのインストール

ElasticsearchにはJavaが必要なのでインストールします。
teratermなどでSSHログインして以下のコマンドを実行します。
Java11を使いたい方は適宜変更してください。*2

$ sudo yum update
$ sudo yum install java-1.8.0-openjdk.x86_64 

Elaticsearchのインストール

次にElasticsearch 7.2.0のインストールを行います。
インストールはrpmファイルを取得し行うことにしますので、以下のコマンドを実行しファイルを取得します。*3

$ wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.2.0-x86_64.rpm

ダウンロードが終わったら以下のコマンドでインストールを行います。
少し待てばインストールが完了します。

$ sudo rpm --install elasticsearch-7.2.0-x86_64.rpm

Kibanaのインストール

Elasticsearchと同様の手順でインストールが出来ます。
今回はKibana 7.2.0をインストールします。

$ wget https://artifacts.elastic.co/downloads/kibana/kibana-7.2.0-x86_64.rpm

ダウンロードが終わったら以下のコマンドでインストールを行います。
少し待てばインストールが完了します。

$ sudo rpm --install kibana-7.2.0-x86_64.rpm

Kibanaへの外部からのアクセスの許可設定

Kibanaはデフォルトの設定ではlocalhostからのアクセスしか出来ないので、設定を変更し、外部からも閲覧可能な状態にします。
/etc/kibana/kibaya.ymlを編集し、以下の設定を入れます

$ vi /etc/kibana/kibaya.yml

以下の設定を入れます。

server.host: "0.0.0.0"

サービスの起動

インストールと初期設定が終わったら、サービスを起動します。

Elasticsearchのサービスの起動

Elasticsearchはserviceコマンドから実行可能です。

$ sudo service elasticsearch start

1分程で立ち上がるので、以下のコマンドを実行してみます。

curl localhost:9200/

以下のように結果が返ってくれば成功です。
f:id:mamepika:20190729213708p:plain

Kibanaのサービス起動

KibanaもElasticsearchと同様、serviceコマンドで実行可能です。

$ sudo service kibana start

こちらはElasticsearchほど時間がかからないので、30秒ほどで起動すると思います。
Webブラウザ
http://< EC2の外部IPアドレス>:5600にアクセスすれば以下のような画面が表示されると思います。
f:id:mamepika:20190729213936p:plain
f:id:mamepika:20190729214053p:plain

*1:Elastic IPも取得しておくと便利です

*2:Java11の指定方法が分かりませんでした。

*3:新しいバージョンが出ている場合は"7.2.0"の部分を変更してください