JDK7→JDK8にバージョンアップした時の作業まとめ

12/25 追記

JDK8でmaven releaseプラグイン経由でJARを生成するときにjavadocを作成しててエラーになった。

maven-javadoc-pluginのconfiguration <additionalparam>-Xdoclint:none</additionalparam> 

を追加した

サーバ構成

インフラの構成ファイルはchefで管理

当初の予定

以前、他メンバーがJDK8にアップデートできるか検証してくれていて、NewRelicが対応していなかったため断念したという経緯があったので、そんなに変更が無いだろうと判断。

  1. 開発環境で軽く検証
    1. JDK7でコンパイルしたwarをJDK8上で動くか
    2. 今のアプリをJDK8でコンパイルできるか
  2. サーバ1台にJDK7でコンパイルしたwarをJDK8上で動くか検証する。問題があれば即座にロードバランサーから切り離す。
    1. NewRelicやMunin,Zabbixの確認
  3. 問題なければ全サーバへJDK8をアップデート(アプリはJDK7でコンパイル)
  4. JDK8でコンパイルしたアプリをデプロイする (←ここはまだ)

もちろんすべての動きを開発環境で確認できるのがベストだが無理かつ、他システムと連携している部分は最終的には本番環境でしか確認できない。
よって、実際に稼働しているサーバに入れて、ログを監視して問題があれば、ロードバランサーからサーバを切り離してユーザがアクセス出来ないようにする方針を取った。

開発環境上でハマったところ

  1. コンパイルが通らない
    • lombokのバージョンが古かったので最新にバージョンアップ
  2. テストがコケる
  3. Jenkins(Version 1.555)がJDK8で起動しない
    • Jenkinsを最新に変更した

Springのバージョンアップが入った時点で予定変更

  1. 開発環境上で検証
  2. Springのバージョンを上げた状態のアプリがJDK7上で動くか確認
  3. 上記のアプリがJDK8上で動くか確認
  4. 問題なければ全サーバへJDK8をアップデート(アプリはJDK7でコンパイル)
  5. JDK8でコンパイルしたアプリをデプロイする

本番環境へ入れてみてハマったところ

  1. 特定の外部システムとHTTPS通信している箇所でSSLHandshakeExceptionが発生した
    • HTTPSでなくても問題ないとのことだったので、HTTPへ変更
  2. JVMメモリ上にキャッシュさせているimmutableなマスタデータを各リクエスト時に再度ソート、フィルタしてた。JDK8でListやCollectionsの実装などが変わってたので見つかった
    • プログラム直す

性能/JVMパラメータ

JDK7でコンパイルしたアプリをJDK8上で動かしても特に性能落ちていない。
JVMパラメータなどはPermSize→MetaspaceSizeに変更して、MaxDirectMemorySizeを追加したぐらいで、G1GCは入れてない。
問題が起きた時に何が原因かわからなくなるので、変更は最小限にしたかった。
G1GC導入は他の優秀なエンジニアがやってくれるはず|д゚)チラッ

感想

  • 当初、他作業と平行してやろうと思ってたが、やっぱり無理だったので、この作業だけに専念できたのは大きかった
  • 社内のプライベートクラウドが便利だった。一人あたり5CPU分つかえたので、JDK7とJDK8を入れたサーバを用意して、それぞれで問題ないか検証してた。
  • SpringのバージョンアップCVE-2014-3578 Directory Traversal in Spring Frameworkの問題が見つかったので、Springのバージョンを3.2.12にあげて正解だった。

参考にしたもの

WEB+DB PRESS Vol.82

WEB+DB PRESS Vol.82

  • 作者: 山口徹,Jxck,佐々木大輔,横路隆,加来純一,山本伶,大平武志,米川健一,坂本登史文,若原祥正,和久田龍,平栗遵宜,伊藤直也,佐藤太一,高橋俊幸,海野弘成,五嶋壮晃,佐藤歩,吉村総一郎,橋本翔,舘野祐一,中島聡,渡邊恵太,はまちや2,竹原,河合宜文,WEB+DB PRESS編集部
  • 出版社/メーカー: 技術評論社
  • 発売日: 2014/08/23
  • メディア: 大型本
  • この商品を含むブログ (1件) を見る

Flap◯ Birdを作ってみる。その2

鳥のアニメーションを表示する

予め lv1_bird_normal.png, lv1_bird_flyup.png, lv1_bird_flydown.png を用意します。
shoeboxを使って、画像を結合します。

別レイヤーを用意してそこに実装します

    auto birdSprite = Sprite::create();
    birdSprite->setPosition(0,0); // 中央に表示したい。相対参照なので(0,0)
    auto birdCache = SpriteFrameCache::getInstance();
    birdCache->addSpriteFramesWithFile("images/birds.plist");
    vector<string> birds = {
        "lv1_bird_normal.png",
        "lv1_bird_flyup.png",
        "lv1_bird_flydown.png",
    };
    
    auto birdAnimation = Animation::create();
    for(string str : birds){
        SpriteFrame *sprite = birdCache->getSpriteFrameByName(str);
        birdAnimation->addSpriteFrame(sprite);
    }
    birdAnimation->setDelayPerUnit(0.25f); // 遅延
    birdAnimation->setRestoreOriginalFrame(true); // 画像を元に戻す
    auto birdAnimate = Animate::create(birdAnimation);
    birdSprite->runAction(RepeatForever::create(birdAnimate)); // 繰り返す
鳥に重力を入れる

createScene関数のcreateWithPhysicsから下3行で重力の方向を入れてます。
また、body->setMassで重さ入れてます

#include "GameLayer.h"
using namespace std;

USING_NS_CC;

static const Vec2 GRAVITY = Vec2(0, -50);  // 重力
static const int BIRD_ROTATION_ANGLE = -10; // 鳥の角度
static const float BIRD_MASS = 5.0f;        //

Scene* GameLayer::createScene(){
    auto scene = Scene::createWithPhysics();
    auto world = scene->getPhysicsWorld();
    world->setGravity(GRAVITY);
  
    // DEBUG mode
    scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
    
    // 'layer' is an autorelease object
    auto layer = GameLayer::create();

    // add layer as a child to scene
    scene->addChild(layer);

    // return the scene
    return scene;
}

bool GameLayer::init(){
    if(!Layer::init()){
        return false;
    }
    
    Vec2 origin = Director::getInstance()->getVisibleOrigin();      // 0座標

    // 鳥のアニメーションの動き
    auto birdSprite = Sprite::create();
    birdSprite->setPosition(0,0);
    auto birdCache = SpriteFrameCache::getInstance();
    birdCache->addSpriteFramesWithFile("images/birds.plist");
    vector<string> birds = {
        "lv1_bird_normal.png",
        "lv1_bird_flyup.png",
        "lv1_bird_flydown.png",
    };
    
    auto birdAnimation = Animation::create();
    for(string str : birds){
        SpriteFrame *sprite = birdCache->getSpriteFrameByName(str);
        birdAnimation->addSpriteFrame(sprite);
    }
    birdAnimation->setDelayPerUnit(0.25f); // 遅延
    birdAnimation->setRestoreOriginalFrame(true); // 画像を元に戻す
    auto birdAnimate = Animate::create(birdAnimation);
    birdSprite->runAction(RepeatForever::create(birdAnimate)); // 繰り返す
    auto material = PHYSICSBODY_MATERIAL_DEFAULT;
    material.restitution = 0.0f;
    material.density = 1.0f;
    material.friction = 1.0f;
    auto body = PhysicsBody::createBox(birdSprite->getContentSize(), material);
    body->setRotationEnable(true);
    body->setMass(BIRD_MASS);
    body->setContactTestBitmask(true);
    birdSprite->setPhysicsBody(body);
    this->addChild(birdSprite,2);
    
    return true;
}
タッチイベント

上の一番最後のthis->addChildのところから

    // タッチイベント
    auto touchEventListener = EventListenerTouchOneByOne::create();
    touchEventListener->setSwallowTouches(true); // タッチイベントの上のレイヤーに影響を与えない
    // ラムダ関数 すべての外部変数は、もし使われれば暗黙的にコピーされる
    touchEventListener->onTouchBegan = [=] (Touch* touch, Event* event){
        log("call touch event");
        birdSprite->getPhysicsBody()->applyImpulse(Vect(0, 180.0f),
                                                   Vect(0, birdSprite->getContentSize().height * (-1)));
        // 角度を設定.
        birdSprite->setRotation(BIRD_ROTATION_ANGLE);
        return true;
        // もしかしたらこちらのほうがいいかもしれない.
//        // TODO: 落ちてる時の角度を戻す
//        birdSprite->runAction(MoveTo::create(0.16f, Vec2(birdSprite->getPositionX(), birdSprite->getPositionY()+BIRD_ADD_POSITION_Y)));
//        // TODO: 一番上に行った時の制御 -> いるかな?
//        return true;
    };
    this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(touchEventListener, birdSprite);
鳥をずっと下に行かないようにする

このままだと鳥がずっと下にいくので、地面で受け止めるようにします。
setDynamicをfalseにすることで、重力の影響を受けないようにしてます。また、setContactTestBitmaskで衝突するように設定してます。

    // 画面下の動き
    auto ground = Sprite::create("lv1_background_ground.png");
    ground->setPosition(Vec2(0, (-1) * (origin.y + visibleSize.height/4)));
    ground->setScale(0.85714f, 1.0f);
    auto groundPhysicsBody = PhysicsBody::createBox(ground->getContentSize());
    // 重力の影響を受けないようにする.
    groundPhysicsBody->setDynamic(false);
    groundPhysicsBody->setContactTestBitmask(true);
    groundPhysicsBody->setTag(1);
    ground->setPhysicsBody(groundPhysicsBody);
    this->addChild(ground, 1, 1);

Flyway Rollupをつくった

https://github.com/hachi-eiji/flyway-rollup

仕事でFlywayを使ってるのですが、いくつが問題が出てきて

  • 開発が進むとファイルが増える(V100とかまでできた)
  • flywayのlocationsを安定版(本番で稼働中)と開発用(自分が開発中)でディレクトリを分けた場合に、

他の人が安定版ディレクトリにpushした後に,flyway:migrateを叩いてもversion番号が進んでいるため安定版の内容が反映されない

という問題があって、毎回flyway:init → migrateを実行してると、initで消されると困るデータ(管理画面から入力されているデータ)が出てきました。
ということで、

  • 最新の安定バージョンの番号のファイルにこれまで流したスクリプトファイルをマージ
  • schema_versionテーブルの中からdevelopment ディレクトリの中にあるものだけ削除する.

というのを作ってみました。想定している動きとしては

一定期間でstableディレクトリをrollupする(Jenkinsなどでやらせたい)

  1. 開発者は各featureブランチへマージ
  2. flyway devCleanを実行するとschema_versionの該当レコードは消える
  3. lyway migrateを実行するとrollupしたスクリプト + developmentのところは実行される -> 開発可能状態

現状の課題は、rollupしたファイルのchecksumがschema_versionテーブルのchecksum列とずれること。flyway3.0のデフォルト挙動は最初にvalidateを行ってからmigrateを実行するのでエラーが起きます。

他の人達はどうやってるんだろうなぁ

Flap◯ Birdを作ってみる。その1

会社から上記ゲームをつくってみなさいというお題が出されたのでcocos2d-xで挑戦中。Unityはなんか自分の感覚に合わなかったので...

まずはインストール

  1. cocos2d-x 自体のインストール
  2. http://www.cocos2d-x.org/download からダウンロード
  3. Androidのダウンロード https://developer.android.com/sdk/index.html からAndriod SDKと NDKをダウンロードする
  4. ant install(brew install ant)
  5. setup.pyを実行する。パスを聞かれるので適宜入力する

お約束のHelloWorld

cocos new -p com.hachiae.cocos.helloword -l cpp HelloWorld
cocos run -p ios

Andriodいれたものの案の定、エミュレータの起動が遅すぎるので一旦iOSで動作確認。

とりあえずここまではできた。

インストール時にチュートリアル的なものがあるのかなーと探してたけど探しきれなかったので、ゲーム作りながら必要な物を調べていく方針に変更。
となると、タスクを洗い出そうということで、洗い出しました。

  • #1: タップすると鳥を浮き上がらせる
  • #2: 障害物の当たり判定と通過判定を行う
  • #3: ゲームオーバー時に得点を表示する
  • #4: 背景をスクロールして動いている感じに見せる
  • #7: 土管画像を表示する
  • #5: ステージを無限に見せる
  • #6: 背景画像を表示する

背景画像の表示

cocosコマンドを実行して作成されたHelloWorldScene.cppに記載していく。
cocos2d-xのx,y座標の開始位置は左下から開始というところを念頭に置きつつ,positionで中央座標を設定。
大学時代にC++を勉強してたけどなんかだいぶ違うw

    Size visibleSize = Director::getInstance()->getVisibleSize(); // 画面解像度
    Vec2 origin = Director::getInstance()->getVisibleOrigin();      // 0座標
    auto backGround = Sprite::create("background.png");
    backGround->setPosition(Vec2(origin.x + visibleSize.width/2, origin.y + visibleSize.height/2));
    this->addChild(backGround, 0);

鳥の動いているアニメーションを表示

ShoeBoxを使って複数の画像(ここではbird000.png〜bird.002.png)を1つの画像にまとめた後にアニメーションの設定をおこなう。

    // 鳥のアニメーションの動き
    auto birdCache = SpriteFrameCache::getInstance();
    birdCache->addSpriteFramesWithFile("birds.plist");
    
    auto birdSprite= Sprite::create();
    birdSprite->setPosition(Vec2(origin.x + visibleSize.width/2, origin.y + visibleSize.height/2));
    this->addChild(birdSprite,1);
    
    auto birdAnimation = Animation::create();
    for (int i = 0; i < 3; i++) {
        auto str = __String::createWithFormat("bird00%d.png",i);
        SpriteFrame *sprite = birdCache->getSpriteFrameByName(str->getCString());
        birdAnimation->addSpriteFrame(sprite);
    }
    birdAnimation->setDelayPerUnit(0.25f);
    birdAnimation->setRestoreOriginalFrame(true); // 画像を元に戻す
    
    auto birdAnimate = Animate::create(birdAnimation);
    birdSprite->runAction(RepeatForever::create(birdAnimate)); // 繰り返す

色んなところから情報あつめて来て理解しながらやると、やっぱり開発速度遅くなるなー

GitHubEnterpriseをどうやって仕事に使っているか

で、GitHubKaigiから1周間も経ってしまったのですが書いておきます。
スマホ向けのソーシャルゲーム(Webブラウザ)を提供しているので、頻繁にリリースが発生はしますが、定期リリース日というのが決まっているので、git-flowで開発しています。今のところこれが合ってるのかなーと

あとは個人リポジトリがあるので、そこにdotfile毎日のメモ書きやシェルを残したりしています。最初はEvernoteでもいいのかなーと思ってたのですが、Vimの重力に魂を引かれた身としては、Vimバインド使えないとちょっと面倒だったり、シェルとかプログラムだとやっぱりテキストじゃないと正直つらいっす

仕事と絡んでるところとしては、今は毎日午前中に一人カイゼン時間というのを設けてます。出社後にNewRelic/Muninを見てバグ/処理が遅い部分を潰すようにしています。ゆるいルールは

  • なるべく午前中に終わらせる、終わらなかったら翌日へ。
  • 一人で完結できるようにする
  • できるだけテストコードを書く

ということにしてます。
長期間の開発をやっていると飽きが出てくるので気分転換がてらにやってます。
ここでもGitは大活躍で、

  1. ブランチ切る
  2. 開発/テスト書く。細かいものなので仕様わかる/テスト書きやすい
  3. 終わらなかったら他の仕事のブランチをcheckout

SVNの時はサーバ側へcommitするしかないので、本当にこれは革命だよなーと。ダメだったら翌日revetすればいいだけだし。

GitHub Kaigi 感想文

6/1(日)に GitHub Kaigiに参加してきました。運営のみなさん素晴らしい会議ありがとうございました!その中でいくつか全体的な感想と印象に残ったものをまとめます

全体的な感想

id:naoyaさんもクロージングで言っていたように、昔コードレビューやリモートワークやろうとしたけど挫折したけど、今はそれが簡単にできるようになったというのはすごいなと。自分も前職(SIer時代)でRedmineやコードレビューを導入しようと思ったけど、結局Redmineしか導入できなかった(しかも数名のチーム限定)という残念な経験をしたことがあります。(あとはVSSからSubversionに乗り換えたかった)

もちろんGit自体もいいものだと思ってるのですがGitHubがそれを後押ししてるのだと感じてます。ちょうどRuby on Railsが爆発的人気になったからRubyもさらに人気言語になったと同じような感じでしょうね。参加者500人(さらに参加待ちが300人とか?)というのが、それを物語っているイベントでした。

特に印象に残ったセッション

はてなブログチームの開発フローとGitHub
Redmindメイン→GitHub Issues→GitHub Issues+ホワイトボードのカンバンという変遷が「なるほど」と思わされました。ワークフローがある程度固まるとなかなか変えづらいのですが、はてなというエンジニア集団かつ少人数チームなのでドンドン変えていけるのかなーと羨ましかったです。うちのチームはサーバサイドとフロントエンドでリポジトリが別々なので、なかなかGitHub Issuesにするのは難しくJIRAとGitHubEnterpriseを紐付けてます。(個人的は見るものが少ないほうが嬉しいのでIssuesのほうがすき) はてなブログチームは複数リポジトリとかで運用してないのかしらん

pplog.net の作り方 ( 〓ω〓) ゆるふわ development on GitHub
「ポエム駆動開発」この一言につきますね。サービスのコンセプトをぶらさずに明示しておくというのが、言うは易く行うは難しですね。ここに書いてないのは枝葉だから切り捨ててもいいよねと。以前どこかで見たことがあるのですが、最初にリリースノートを書く(Amazonだったかな?)という表現をポエムぽく。うちのプランナーさんにも見てほしいです、はい。

個人的にどうGitHubEnterpriseを使ってるかは次のエントリに書きます

Agile Samurai Base Camp 2014 Re:TDDで学んだことを反映してみる

Agile Samurai Base Camp 2014 Re:TDDに参加してきました - 個人的なまとめでかいたようにJUnit+Grooy+Spockでテストのテンプレを書いてみました。
コードはこちら
https://github.com/hachi-eiji/java-spock-practice

正直Groovyは初めて書いた(ネットで落ちてる資料を調べただけ)のでぶっちゃけGroovyぽくないと思いますのでそこら辺は勘弁。今所属しているプロジェクトでほぼ同じコードのプルリクエスト投げていてレビュー中。

なぜ試してみようと思ったかというとテストコードが非常に膨れ上がってたのです。
1つのサービスクラスのテストをつくろうとした時に8割がモックの設定ばかりで非常に楽しくない。かつ、同じメソッドの境界値試験をするときにそれぞれの境界値で複数メソッドを作らないといけない。楽しくないから生産性あがらないことが多いなぁと思ってました。

そんなときにAgile Samurai Base Camp 2014 Re:TDDに参加してきて、JUnit+Grooy+Spockのデモ拝見した時にこれだったら少しは楽になるかなと。ちょうどJavaを使っててテストコードだけだったらユーザに迷惑かけないから勝手につくちゃおうと。朝会でちょっとこれ試したいからーと宣言してやっちゃいました。(もちろん本来の仕事もしてましたよ)

気をつけたのは2点。

  1. このやり方を強制しない
    • いまはJUnit+JMockitで作ってるのですが、それはそれでいいかなと思っています。好きなほう(楽なほう)をつかえばいいのかなと思ってます。自分もたまたま今回使った言語がGroovyだっただけだし、宗教上の理由でGroovy使えない人もいるかもしれないしw
  2. 既存のテストクラスを何点か移植してきて、サンプルコードとコメントを結構注意深く書いた
    • 強制しないとはいえやっぱり使ってほしいし、何よりテストを楽に書いてほしい。なので、最初に書くところでハードルになってはいけない(これもBase Campで再認識したところ)と思ってだいぶ細かく書きました(あとでGitHubにあげよう)。例えば、given:はテスト実行前に呼ばれるsetUpと同じでwhereはこうでとか。モックを使ったときとか。