量産型エンジニアの憂鬱

きっと僕は何物にもなれない。

Java Day Tokyo 2016に行ってきました

JJUG CCCに続いて、Java Day Tokyo 2016に行ってきました。

今回は教育扱いで参加させていただきました。感謝!

聴講したセッションはこんな感じ。
めっちゃJava SEでした。

f:id:duck8823:20160525233224p:plain

とりあえずまとめておきます。
英語できないし、同時通訳馴れないし、椅子でお尻痛いし、CO2濃度高いしで疲労しました。
だもんで、色々間違ってるかも。

基調講演

こんな感じの流れ。

Javaの現状

30億のデバイスで走るJava
Java SE8の大成功 => 首位返り咲き

JCPへの招待

Java Community Process(JCP)
NPO, ユーザグループ, 個人, 企業が参加している

成功のカギは我々開発者に!!!

プラットフォームと開発

メインプラットフォームはクラウド!!!

クラウド・・・大きな一つの流れですね。

Java SE9

モジュール、REPLなど
これはもうちょい詳しく

ツール

NetBeansのデモ
JavaScript用のツールキットのデモ
これめっちゃよかった。

サービス

時代はクラウド

  • マイクロサービス
  • 分散化
  • Polyglot(多言語)
  • DevOps
  • コンテナ

Java EE

Java EEモノリスでも、マイクロサービスでもいけるよって話?

Java EE事例

システム再構築でJavaを採択するよ!
COBOL -> Java(JSF + CDI + JPA)

エンジニア募集してた。
商業高校でCOBOLならって(覚えてない)今Javaを愛してる僕をぜひ・v・

ドローンデモ

ドローンで情報収集させてクラウドにぶち込んで、ブラウザで見る。
これ、研究分野で使えるね。

ペッパーデモ

Dukeは4本指

Java SE9 Overview

クラウド時代の戦略

  • Security
  • Density
  • Start-up time
  • Predictability
  • 生産性

SE9のメインはモジュール(Project Jigsaw)
デフォルトのガベージコレクションC1GCに -> スケーラビリティ
REPL(JShell) -> インタラクティブモードで教育

SE9以降・・・Value Typesとか。

Project Jigsawではじめるモジュール開発

module-info.java をクラスパス上に置く。
書き方はこんな感じ?

module com.foo.app {
    requires com.foo.bar;
    requires public java.sql;
    exports com.foo.app;
}

requiresで依存関係かく。requires publicで依存関係の依存関係も。
exportsで別モジュールから見ていいの指定できる。
今のとこ、バージョンはコマンド引数で指定・・・。

JavaFX 8 and the future

Javaでデスクトップアプリ作るならSwing -> JavaFX

Java FX8では・・・

  • HTML5とかCSS使える
  • Fluon Scene Builder8で作る

実践して分かったJavaマイクロサービス開発

マイクロサービスを実践してきた事例を追体験

start simple, not small いい言葉。

教科書通りにやる必要はない。チームにあった選択を。

マイクロサービス流行ってるけど、
うちは、

  • 開発メンバー一人+スポット
  • オンプレミス
  • ダウンタイムあってもいい

て感じなんで、慌てて採用しなくてもいいと思った。

オラクルコンサルが語るJava SE 8新機能の勘所

Java SE8の有名どころAPI

のパフォーマンス紹介

パフォーマンスが悪くなるってことはない
Date and Time APIのほうがCalendarよりセキュアだったり
Stream APIは簡単にパラレルにできたり
API以降してった方がお得のよう。

パフォーマンスはJava Flight Recorderで測定できる。
あと、パラレルっていってもハイパースレッディング分は速くならない

・・・パラレルしてる処理がCPUフルに使えないようなやつなら、ハイパースレッディング分も早くなりそうかな?あれってたしかCPUの余ってる部分の効率よく使う仕組みだった気がするから。

まとめ

お尻痛い。
JJUGよりもフォーマルな人多かった。

Jigsawのこと聞けて満足。
あとパフォーマンス測定とかしたことなかったし、Flight Recorderとか使ってみようと思いました。

JJUG CCC 2016 Springに行ってきました。

JJUG CCC 2016 Springに行ってきました。

技術系カンファレンスは4度目の参加です。
今回の自分の中でのテーマは、「JJUG春のアップデート祭り」です。

以下、聴講したセッション。
自分に関係ありそうなところをかいつまんでメモ。

テスト自動化のまわりみち

@irofさんのセッション。

今までの手動テストのコスト

設計 + 実装 < 実施 + 検証

自動テストのコスト

設計 + 実装 > 実施 + 検証

今までの手動テストの設計はふわっとしてる分、実施とか検証で人間の判断が入ってた。
手動テストの設計でテストコードを実装しようとコードに起こせなくて詰む。
詰んだら失敗して将来自動化しようとする人の壁になりかねない・・・。

調べる技術リスト

  • AssertJ : 流れるインターフェース(チェーン)でテストをかける。

Thymeleaf3を使ってみよう!

@bufferingsさん。

Thymeleaf3でたよ。

  • パフォーマンスがめっちゃ上がってる
  • HTML5サポート
  • TEXTとJavaScriptもサポート
  • レイアウト
    • fragment expression ~{...}追加
    • th:fragmentにパラメータ持たせられる

メールも扱うアプリなのでテキストを扱えるのは嬉しい。 テンプレートエンジンを統一できるかな。

Jenkins2.0

@kohsukekawaさん。

Jenkins 2

目玉はパイプライン

  • GUIでどこでコけたとかわかりやすい
  • インフラのコード化 => Jenkinsfile

Organization Folder

  • Organizationを指定すると、自動的にいろいろ設定してくれる。

テストゼロからイチへ進むための戦略と戦術

渡辺 祐さん。

自動テストコード書く時の指針になりそうなお話。

Spring Framework/Bootの最新情報とPivotalが進めるクラウドネイティブなアプローチ

@makingさん。

Spring Boot 1.4

  • banner.pngでバナー表示
  • テスト回り
    • SpringRunner.class, @SpringBootTest, @LocalServerPort
  • Spring 4.3
  • 暗黙のコンストラクタインジェクション(Lombokとの相性いい), @RequestMapping->@GetMappning @PostMappingとか, @Scope->@SessionScope @RequestScope

Beatsシリーズでお手軽メトリック収集可視化

@johtani

Beatsシリーズ
logstashforwaderはメンテされなくなって、filebeatになった。

ログ収集するエージェントたち

  • packetbeat : network, PostgresqlSQLとか
    • kibanaのダッシュボードもテンプレがある
  • topbeat : システムの値
  • filebeat/winlogbeat

GoLangでbeatは作れる!

まとめ

  • SpringBoot、Jenkins、Thymeleafのアップデート。
  • LogstashForwarder -> Beats。
  • テスト系使えそうなツール類新規知見
    • SonarQube
    • AssertJ
    • MailCatcher

JPA使ってデータベース初期化するときに同じクラスの@Embeddedが重複してる場合の対応

早速ですが以下のようなクラスがあったとします。

@Entity
class User {
    @Id
    private Long id;

    private String firstName;

    private String lastName;

    private String firstNameOfRecipient;
    
    private String lastNameOfRecipient;
}

@Embedded@Embeddableを使って分解したいですね。

@Entity
class User {
    @Id
    private Long id;

    @Embedded
    private Name name;

    @Embedded
    private Name recipient;
}

@Embeddable
class Name {
    private String firstName;

    private String lastName;
}

これをSpringBootのspring.jpa.hibernate.ddl-autoを使って生成した場合に以下のようなエラーになりました。

Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: com.duck8823.model.hoge.User column: first_name (should be mapped with insert="false" update="false")
    at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:709) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
    at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:731) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
    at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:727) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
    at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:753) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
    at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:506) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
    at org.hibernate.mapping.RootClass.validate(RootClass.java:270) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
    at org.hibernate.cfg.Configuration.validate(Configuration.java:1360) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1851) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:857) ~[hibernate-entitymanager-4.3.11.Final.jar:4.3.11.Final]
    ... 29 common frames omitted

フィールド名でカラムを作成するのですが、下のソースコードの場合Name型のnamerecipientもどちらもfirstNamelastNameカラム名を生成しようとするので競合してしまいます。
これを回避するには、@AttributeOverrides@AttributeOverrideを利用して、こんな感じに書くといいです。

@Entity
class User {
    @Id
    private Long id;

        @Embedded
    private Name name;

    @AttributeOverrides({
        @AttributeOverride(name = "firstName", column = @Column(name = "firstNameOfRecipient")),
        @AttributeOverride(name = "lastName",  column = @Column(name = "lastNameOfRecipient"))
    })
    @Embedded
    private Name recipient;
}

@AttributeOverrideで指定したnameプロパティについて、columnで指定した@Columnでオーバーライドすることができます。

botkit使って定期的につぶやくslackbot作る

Slack便利ですね。

他のチャットツールとの違いとして、連携できるアプリの豊富さがあげられると思います。
さらに、BotkitHUBOTなどを使って簡単にbotが作れてしまいます。
今回はBotkitを使ってSlackのbotを作成してみます。

SlackでBotを登録する

Botの管理ページからAdd Configurationをクリックします。
f:id:duck8823:20160519213830p:plain

botのユーザ名でを入力しAdd bot integrationをクリックします。
f:id:duck8823:20160519213948p:plain

作成したbotの管理画面が開くので、表示されるAPIトークンを覚えておきます。 f:id:duck8823:20160519214046p:plain

Botを作成する

nodejs のインストール

公式のページからインストーラか、ソースコードをダウンロードしてインストールしましょう。

botの作成

botを作成します。本当に簡単。
まずはnpmで初期化。

npm init

いろいろ聞かれるので答えていきます。そうするとpackage.jsonができます。
公式の通り、シンプル。

npm install --save botkit

すると、node_modulesというディレクトリができ、そのなかにbotkitディレクトリができます。
--saveオプションをつけてインストールすることで、package.jsondependenciesにもbotkitを追加してくれます。
package.jsondependenciesに記述することで、次回からはnpm installだけで依存関係がすべてインストールされます。

botの本体を書きます。

vi index.js
var Botkit = require('botkit');
var controller = Botkit.slackbot();
var bot = controller.spawn({
  token: process.env.token
}).startRTM(function(err,bot,payload) {
  // 初期処理
  if (err) {
    throw new Error('Could not connect to Slack');
  }
});
controller.hears(["進捗どうですか"],["direct_message","direct_mention","mention"],function(bot,message) {
  // キーワードに反応した処理
  bot.reply(message, '進捗ダメです');
});

botの起動

botのトークンは、

  token: process.env.token

で指定しています。process.env.tokenは実行時に指定して渡せます。

以下で実行。

token=<確認したtoken> node index.js

するとチーム内のbotがログイン中になります。
f:id:duck8823:20160519220627p:plain

これだけです。簡単。

controller.hears(["進捗どうですか"],["direct_message","direct_mention","mention"],function(bot,message) {
  bot.reply(message, '進捗ダメです');
});

は、進捗どうですかというキーワードがダイレクトメッセージあるいは@付きのメンションでつぶやかれれば、function()内の処理を実行します。
bot.reply(message, 'メッセージ')で返事してくれます。

早速botを適当なチャンネルに招待して、つぶやいてみましょう。
f:id:duck8823:20160519221103p:plain

@hoge進捗どうですかとつぶやいたので、進捗ダメですと返信されました。

定期的につぶやくbotをつくる

定期的な処理を実行するにはnode-cronを利用すると簡単に作ることができます。

npm install --save cron

上記のスクリプトを少し書き換えます。

var Botkit = require('botkit');
var CronJob = require('cron').CronJob;
var controller = Botkit.slackbot();
var bot = controller.spawn({
  token: process.env.token
}).startRTM(function(err,bot,payload) {
  // 初期処理
  if (err) {
    throw new Error('Could not connect to Slack');
  }
  new CronJob({
        cronTime: '* * * * 1-5',
        onTick: function() {
                bot.say({
                        channel: 'general',
                        text: '進捗どうですか',
                        username: 'hoge',
                        icon_url: ''
                });
        },
        start: true,
        timeZone: 'Asia/Tokyo'
  });
});
controller.hears(["進捗どうですか"],["direct_message","direct_mention","mention"],function(bot,message) {
  // キーワードに反応した処理
  bot.reply(message, '進捗ダメです');
});

startRTM()内に初期処理を記述することができます。
その中でCronJonを生成しています。上記例ではcronTime: '* * * * 1-5',を指定してるので平日に毎分ジョブを実行します。
onTickで処理を記述していますが、bot.say()でつぶやくことができます。

  • channel : Slackのチャンネル
  • text : つぶやく文字
  • username : Slackに表示されるユーザ名
  • icon_url : Slackに表示されるアイコンのURL

を指定します。

f:id:duck8823:20160520004902p:plain
とてもうざいボットができました。

作成も実行もお手軽ですね。

アノテーションプロセッサで生成したコードをCompile Testingを使ってテストする

アノテーションプロセッサを使って、SeasarプロジェクトのGen-Namesのようなものを作りました。

github.com

Gen-Namesなどは@Entityが対象ですが、@GenerateNamesアノテーションをクラスをつけるとそのクラスと親クラスのフィールド名をStringで返すようにしました。

アノテーションプロセッサはJavaPoetを使って簡単にできます。
こちらの記事がめちゃめちゃ参考になりました。

JavaFileからソースコードを生成したい場合はJavaFileObject#openWriter()を使ってWriterを取得してやればよい。

アノテーションプロセッサの

javaFile.writeTo(System.out);

を以下のようにする。

try {
    JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(クラスのFQDN);
    Writer writer = sourceFile.openWriter();
    javaFile.writeTo(writer);
    writer.close();
} catch (IOException e){
    throw new RuntimeException(e);
}

単体テストなどで実行した場合、InMemoryJavaFileObjectとして、メモリ上に作られるみたいです。生成されたファイルが見たい!って場合は、javaFile.writeTo()の引数をFileインスタンスにするか、System.outにして確認しましょう。

生成されたコードの単体テストをどうするか。

日本語やと、アノテーションプロセッサのテストはこちらもSeasarプロジェクトのAptina Unitが検索の上位に入ってきますが、近い将来EOLですし。
以下、Compile Testingを使った単体テストをしてみたときのメモ。

依存関係

mavenの場合

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.google.testing.compile</groupId>
    <artifactId>compile-testing</artifactId>
    <version>0.8</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.google.truth</groupId>
    <artifactId>truth</artifactId>
    <version>0.28</version>
    <scope>test</scope>
</dependency>

resourcesディレクトリ

resourcesディレクトリにアノテーションプロセッサ処理対象と、生成されるソースを作成しておく。
今回はHogeクラスを対象に、HogeNamesクラスが生成されることを想定。

f:id:duck8823:20160507085433p:plain


Hoge.java(処理対象)

package net.duck8823;

@GenerateNames
public class Hoge {

    private String hoge;

}



HogeNames.javaアノテーションプロセッサに生成してほしいソース)

package net.duck8823;

import java.lang.String;
import javax.annotation.Generated;

/**
 * Generated by generate-names.
 * @see https://github.com/duck8823/generate-names
 */
@Generated("net.duck8823.GenerateNamesProcessor")
public class HogeNames {
    /**
    * hogeのフィールド名を取得します.
    * @return hogeのフィールド名
    */
    public final String hoge() {
        return "hoge";
    }
}

テストクラス

import com.google.common.io.Resources;
import com.google.common.truth.Truth;
import com.google.testing.compile.JavaFileObjects;
import com.google.testing.compile.JavaSourceSubjectFactory;
import org.junit.Test;

public class ProcessorTest {
    @Test
    public void test() {
        Truth.assert_().about(JavaSourceSubjectFactory.javaSource())
                .that(JavaFileObjects.forResource(Resources.getResource("Hoge.java")))
                .processedWith(new GenerateNamesProcessor())
                .compilesWithoutError()
                .and()
                .generatesSources(JavaFileObjects.forResource(Resources.getResource("HogeNames.java")));
    }
}

コードの生成

生成されたソースの検証

  • コードの生成の記述の後にand()でつなぐ。
  • generateSources()の引数にJavaFileObjects.forResource(Resources.getResource(生成してほしいソース)))で指定する。このファイルの中身が空やとNullPointerExceptionが吐かれる。

生成してほしいソースもリソースとして置いておくことで、変更が楽になるのでよいですね。