メンチカツには醤油でしょ!!

ITエンジニア徒然 (AWS/Java/Node.js/Google Apps Script/Spreadsheets/Docker/Jenkins/コミュニティ・勉強会レポ)

名古屋Javaユーザグループ2018年4月に参加してバイトコードについて考えた #nagoya_jug

地方(?)イベント初参加

よく"出張のついでに地方のイベントに参加"なんて話を聞くことがあり、そんな都合のいい事があるかいな、と思っていましたが、幸運なことにそれが訪れたので初参加です。(勝手がわからないので緊張しますね)

ngo-java.connpass.com

smogamiさん: NJUG紹介・参加者の自己紹介

全員自己紹介するのって初めてだったので緊張。
「飲み歩きが趣味で、バーでたまたま隣に居た人がPHPerでtype safetyでケンカしかけた」など全PHPerを敵に回すようなブッコミをしてしまいスミマセン。事実に変わりないですが、ちょっと面白い事言おうとしただけです。
スクリプト言語好きですよ!)

sh-ogawa さん: テストコードと運用

テストとCI。カバレッジ100%を目指すなとか、エラーを放置するなとか、そんな話。

Sonar Qube使ってみよう。Dockerリポジトリはここ。
https://hub.docker.com/_/sonarqube/

質問でWork In Progress Pull Request開発の話が出ましたが、これってちゃんと用語があってドキュメントに纏まっていたんですね。
https://ben.straub.cc/2015/04/02/wip-pull-request/
PRのタイトルが変わってhookが取れないと質問していましたが、そうなのかな。。

www.slideshare.net

bleit-tift さん: JVM入門

はじめて書いたプログラミング言語コンパイラという猛者の、JVMについてのとてもわかりやすい解説。

CASLとかZ80アセンブリとかをやっていた身としては、バイトコードも結局脳内デバッグとあまり変わらないな、という印象だったのですが、後の発表でさっそく活きる場面が。

skrbさん: Project Amberの話

Project CoinがSmall Lang. ChangesならAmberはSmall to Big Lang. Changesということで、各JEPにより言語仕様がどう変わっていくのか、という話。

Javaは最近では他の言語仕様の影響や、周辺ライブラリの影響を受けて進化している印象がありますが、今回も同じです。

内容については既に公開されているスライドを見て頂くとして、感想としてはJEP 326 Raw String Literalsに変数のバインディングができるともっといいのにと思いました。

var weather = "sunny";
System.out.println(`It's a ${weather} day today.`);

(コンパイルしたらnew StringBuilder().append().append().toString()String.formatと等価になるとか)

www.slideshare.net

yuji38kwmtさん: Java7からJava8に移行した際にClassCastException発生

 現象とかはスライドを見て頂くとして。

qiita.com

P14. Java8ではhogeの型パラメータはchar、とあるが実際はObjectで変わらず。
これはEclipse Compilerの間違い。

バイトコード上でcheckcastが入るJava8だとエラー。
会場でもなぜJava7だと入らないんだろう…もしかしてこっそり修正された?との話も。

下の画像で比較してみました。左側がJava7、右側がJava8です。
確かに右側(Java8)には、59行目にcheckcastが入っているのが確認できます。
ここで、実行時エラーになるわけですね。

f:id:ryoichi0102:20180417123025p:plain

更に見ていくと、ジェネリクスのメソッドhoge()内の定数"hoge"が、Java7ではString、Java8ではcharになっていることが確認できます。

#4 = String #13 // hoge
#13 = Utf8 hoge
#3 = Class #22 // "[C"
#22 = Utf8 [C

コンパイル時のジェネリクスの型消去の際にStringではなくchar[]で生成されるようになっています。こちらも気になります。

f:id:ryoichi0102:20180417123039p:plain

先のセッションでバイトコードの話を聞いていたので、アレルギーなく読めました^^

まとめ

  • javapだいじ
  • javapが少し身近になった

内容の組み合わせや順序がとても良かったように感じました。
懇親会まで行けたら本当は良かったのですが、他にもやることがあったので退散してしまった。会場挙手時点で50%ぐらい挙がってたのが意外(?)でした。

Nature RemoのローカルAPIを叩いてみた

グローバルAPIはこちら

ryoichi0102.hatenablog.com

ローカルAPIも存在する

公式ドキュメントによるとNature RemoのAPIはローカルとグローバルとあり、どちらも内容が違うようなので、ローカルAPIも叩いてみました。

たまたま #nature_remo で検索したら見つけました。機会をくれてありがとうございます。

公式ドキュメントはこちら

http://local.swagger.nature.global/

[ Base URL: remo.local/ ] って書いてあっててっきりローカルネットワーク内で名前解決できるのかと思いましたが違いましたw
よく読むと、Discover Remo using _remo._tcp service.と書いてありました。

% dns-sd -B _remo._tcp

続いて、Resolve IPです。

% dns-sd -G v4 Remo-XXXXXX.local

と書いてあります。

f:id:ryoichi0102:20180315091741p:plain

curlしてみる

ドキュメントに記載の通り、X-Requested-Withが必須だったりとお作法に従います。

curl -X GET -H "X-Requested-With:" -H "Expect:" http://192.168.0.8/messages

f:id:ryoichi0102:20180315091653p:plain

値は返ってきませんでしたが、実行できました。

AWS LambdaでDynamoDBの値が更新されないなぁと思ったらただのスコープの話だった

変数スコープとLambdaのキャッシュが原因でした

AWS LambdaでDynamoDBへの追加/更新処理が複数レコードあり、1度のLambda実行で処理されたレコードの日時は同じ日時がいいなと思い、下記のようなコード書いていましたが

const AWS = require('aws-sdk');
const moment = require('moment-timezone');

// ここで日時を取る
const startedAt = moment().tz('Asia/Tokyo').format('YYYY-MM-DD HH:mm:ss.SSS');

exports.handler = (event, context, callback) => {
  console.log(`Lambda function started at ${startedAt}`);
  // 略
};

どうもDynamoDBの値が更新されないなぁと思ったら…正解はこうでした

const AWS = require('aws-sdk');
const moment = require('moment-timezone');

exports.handler = (event, context, callback) => {
  const startedAt = moment().tz('Asia/Tokyo').format('YYYY-MM-DD HH:mm:ss.SSS');
  console.log(`Lambda function started at ${startedAt}`);
  // 略
};

exports.handlerの外で定義したものは任意でキャッシュが効くような仕様になっていました、これはおそらく実行の最適化のためですよね。

そういえばMySQLでこんなコードを見たんだった。

const mysql = require('mysql');

// ここに定義していればコネクションは破棄されないというメリットが!
let connection = null;

const createSingleConnection = () => {
    connection = mysql.createConnection({ host, user, pass, dbName });
    connection.on('error', (err) => {
        if (err.code === 'PROTOCOL_CONNECTION_LOST') {
            // サーバがコネクションを切った場合は再接続
            createSingleConnection();
            console.log(`Reconnected`);
        } else {
            throw err;
        }
    });
};

// MySQLデータベースへの接続 ここで接続することでコネクションを使いまわせる.
createSingleConnection();

exports.handler = (event, context) => {
  connection.query(sql, (err, rows, fields) => {
    //
  });
};

参考

qiita.com