webpack-dev-server + mkcertで有効なhttpsリクエストでビルド済みのファイルを配信する

kintoneなどのSaaS環境へJavaScriptカスタマイズを行うときカスタマイズ方法が限られているケースがあります.

httpsのURLが登録可能な場合、ローカルの開発環境からhttpsでwebpackでビルドしたファイルを配信すると 開発効率をグッと上げることができます!

そこでwebpack-dev-serverをhttpsで動作させて、変更したファイルを即時配信してみよう.

単純にwebpack-dev-serverをhttpsモードで動作させることはできます. しかし、ブラウザから危険なhttpsリクエストと判断されてwebpack-dev-derverからの JavaScriptのダウンロードに失敗してしまいます.

そこで証明書インストールの便利ツールmkcertを使っていい感じに動作させてみましょう.

mkcert でlocalhostの証明書をインストールする

パッケージマネージャーを使っている場合はmkcert installation を参考にしてください

今回は, ローカルに mkcertをダウンロードして実行する.

chmod +x mkcert-v1.3.0-darwin-amd64 # macOSは実行権限を与える
./mkcert-v1.3.0-darwin-amd64 -install 
# Passwordを求められるので入力
./mkcert-v1.3.0-darwin-amd64 localhost 127.0.0.1 ::1

実行したディレクトリに証明書と秘密鍵が生成されているはずです

webpack-dev-serverをhttpsモードで起動する

サンプルコードはこちらを利用します:

GitHub - yokotaso/kintone-typescript-sample: TypeScriptをつかったkintoneカスタマイズを行うサンプルコードです

webpack-dev-serverをインストールしていない場合はまずこちらを実行します:

npm i -D webpack-dev-server # インストールしていない場合はインストール

実験として、証明書を使わずにhttpsモードだけで起動してみます.

npx webpack-dev-server --https true --mode development

webpack.config.jsのエントリーポイントからURLが決まります

  • webpack.config.jsのentryに指定したentryの[プロパティ名].jsで出力されます.
  • webpack.config.jsのentryにファイルを直接指定している場合はmain.jsで出力されます.

今回のサンプルコードは https://localhost:8080/kintone-typescript-sample.js になります.

アクセスするとブラウザから警告がでてしまいます. f:id:yokotaso:20190411212435p:plain

次に証明書を使ってwebpack-dev-serverを起動します.

mkcertで作成した証明書は/opt/certに配置してあるとします.

npx webpack-dev-server --mode development \
  --https true --key /opt/cert/localhost+2-key.pem \
  --cert /opt/cert/localhost+2.pem

再び https://localhost:8080/kintone-typescript-sample.js にアクセスしてみます. 先ほどの実験のときの警告は消えてビルド済みのJavaScriptが表示されるはずです.

このURLをkintoneカスタマイズに登録するとJavaScriptがリロードするだけでkintoneの開発環境に反映されます.

TypeScriptでkintoneカスタマイズ開発をやってみよう

TypeScriptを使ったkintoneカスタマイズのチュートリアルです。

前提知識

次のツールを使ってチュートリアルを行いますので前提知識となります。

  • node, npm, npx
  • webpack
  • babel
  • TypeScript

設定ファイルはTypeScriptの公式で紹介されているレポジトリを参考にしています。

1. プロジェクトの初期化と必要パッケージのインストール

プロジェクトの初期化を行います。

npm init

プロジェクトの初期化が完了したら開発に必要なパッケージのインストールを行います。

npm install -D @kintone/dts-gen \
                         @babel/core @babel/plugin-proposal-class-properties \
                         @babel/plugin-proposal-object-rest-spread \
                         @babel/preset-env \
                         @babel/preset-typescript \
                         babel-loader \
                         fork-ts-checker-webpack-plugin \
                         typescript \
                         webpack \
                         webpack-cli

@kintone/dts-genはTypeScriptでkintoneカスタマイズおよびプラグイン開発を行うために必要なツールです. 参考: @kintone/dts-gen - npm

@kintone/dts-gen はkintoneのアプリ情報から型情報を生成するコマンドラインツールと kintone上に定義されている組み込みメソッドの型情報が定義されています。

2. kintoneのアプリ情報から型情報を生成する

コマンドラインから次のコマンドを実行してフィールドの型情報を生成します。 kintone側でフィールドの変更を行った場合は、再度生成してください。

npx @kintone/dts-gen --host http://***.cybozu.com \
                 -u username \
                 -p password \
                 --app-id 12 \
                 --type-name SampleFields \
                 --namespace company.name.types \
                 -o src/sample-fields.d.ts

基本的なオプションは次のとおりです。

  • --host ... カスタマイズする予定のURL
  • -u ... ユーザー名
  • -p ... パスワード
  • --app-id ... カスタマイズするID
  • --type-name ... 出力する型名(未指定の場合Fields)
    • 保存ずみの型としてSavedFields, 保存前の型としてFieldsが生成されます
  • --namespace ... 出力する名前空間 (未指定の場合 kintone.types)
  • -o ... 出力するファイル名

--help で詳細を確認できます. なお、--demo というオプションでサンプルの型情報を生成することもできます。

3. tsconfig.jsonを作成する

TypeScriptのコンパイル設定ファイルである tsconfig.json を作成します。1 プロジェクトに合わせて修正してください。

{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "noFallthroughCasesInSwitch": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "esModuleInterop": true,
    "noUnusedLocals": true,
    "noImplicitAny": true,
    "declarationDir": "dist/types",
    "declaration": true,
    "target": "es5",
    "module": "es2015",
    "strict": true
  },
  "files" : [
    "./node_modules/@kintone/dts-gen/kintone.d.ts",
    "./src/sample-fields.d.ts"
  ],
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "dist",
    "node_modules"
  ]
}

kintone上で利用できるグローバルメソッドが@kintone/dts-gen/kintone.d.tsに入っていますので、(2)で生成した型情報とともにfilesで指定するとコンパイル時に型情報を参照してくれます。

4. webpack.config.jsを作成する

src/kintone-typescript-sample.ts というファイルをエントリーポイントにサンプルプログラムを作成します。それに合わせてwebpack.config.jsを作成します。2

const path = require('path');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');

module.exports = {
  entry: {
    "kintone-typescript-sample" : "./src/kintone-typescript-sample.ts"
  },

  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist'),
  },

  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.json']
  },

  module: {
    rules: [{ test: /\.(ts|js)x?$/, loader: 'babel-loader', exclude: /node_modules/ }],
  },

  plugins: [
    new ForkTsCheckerWebpackPlugin(),
  ]
};

.babelrcも作成します . 3

{
  "presets": [
    "@babel/env",
    "@babel/typescript"
  ],
  "plugins": [
    "@babel/proposal-class-properties",
    "@babel/proposal-object-rest-spread"
  ]
}

5. 簡単なプログラムを書いてみる

意味があるプログラムではないですが、TypeScriptでプロパティ参照してもコンパイルに成功します。

const HANDLE_EVENT = "app.record.create.show";
interface KintoneEvent {
    record: kintone.types.SavedFields;
}
kintone.events.on(HANDLE_EVENT, (event: KintoneEvent) => {
    event.record.Number.value = "1";
    event.record.Table.value[0].value.Number_Table.value = "1";
});

プログラムをビルドしてみましょう。

npx webpack-cli --mode development

次のような出力がでてきたらビルドの成功です。

Starting type checking service...
Using 1 worker with 2048MB memory limit
Hash: 2b19dc578431992634bb
Version: webpack 4.29.3
Time: 2010ms
Built at: 2019-02-14 21:58:08
                       Asset      Size                     Chunks             Chunk Names
kintone-typescript-sample.js  4.15 KiB  kintone-typescript-sample  [emitted]  kintone-typescript-sample
Entrypoint kintone-typescript-sample = kintone-typescript-sample.js

2019年のkintoneカスタマイズはTypeScriptに挑戦してみてはいかがでしょうか?

チュートリアルは以上です。お疲れ様でした。今回のサンプルコードはgithubにおいてあるので参考にしてみてください。

github.com

おまけ

静的型付けのおおきなメリットとしてIDEの補完機能があります。VSCodeの補完の様子を見てみましょう

f:id:yokotaso:20190214222737g:plain
interfaceを定義する

f:id:yokotaso:20190214223948g:plain
kintone.event.onを呼び出す

f:id:yokotaso:20190214224001g:plain
コンパイルエラー

f:id:yokotaso:20190214224736g:plain
サブテーブルの参照もラクラク

JJUG CCC 2018 Springで「ざっくりわかった気になるモダンGC入門」というお話をしてきました

JJUG CCC 2018 Springにて、「ざっくりわかった気になるモダンGC入門」というお話をしてきました。

発表資料や喋った内容は次のリンクを参考にしていただければと思います

ここでは、この発表を作るまでの振り返りなどを簡単に書きたいと思います

発表の着想

今年の2月くらいにTwtterでEpsilonGCというGCがあるらしいと流れてきて調べたところ、拍子ぬけしたのがそもそもきっかけだった記憶があります。

ZGCとShenadoah GCも見つけて、EpsilonGCをオチに使う小噺をしたら面白そうというのがそもそも着想でした。

CFPに応募した理由

新しいGCアルゴリズムを勉強したかったのが第一にはあります。ですが、サボり癖を発症してしまう自分がいたので強制力を持たせるようにCFPに応募しました。非常に私的な理由です。

運用で使っていたり、R&Dの業務ということは全くありません。ゴールデンウィークなどの余暇を利用した自由研究です。

登壇までにやったこと

  1. 4月の後半 くらいから本格的にShenandoah GCのホワイトペーパーを読み始める
  2. ゴールデンウィークでZGCのアルゴリズムを勉強
  3. 発表の一週間前くらいに資料が9割完成。社内で発表練習兼勉強会を開く
  4. 当日に社内で一人でリハーサル
  5. 遅刻にひやひやしながらもなんとかベルサール新宿に到着。11:20に登壇開始
  6. 登壇終了

GCアルゴリズムの勉強の進捗が思わしくなく、プレッシャーのかかるゴールデンウィークにw

ZGCのカラーポインタと仮想メモリの部分の理解ができなくて焦っていました。

そんなときに Linuxの仕組み仮想メモリの章を読んでやっと理解できました。

私の知っている限りOSに関する本は難解なものや入門書が多い中で、このLinuxの仕組みは初心者から中級者あたりが理解したいことが掲載されていて、大変素晴らしいです。

サイボウズの技術顧問をやっていただいている方が著者なのでステマ感がありますが、いい本です。

僕が知っているOSに関して初心者が読みやすい本は大規模サービス技術入門 ―データ構造、メモリ、OS、DB、サーバ/インフラ なんですが、これも素晴らしい本です。

発表のレベル感

発表のレベル感的には、説明はざっくりだけどGCアルゴリズムの肝をメインに説明することで動作イメージが共有できるくらいを目標にしていました。

GC絡みでトラブルになったときに頭の中に思い浮かべるメンタルモデルとして想像できるようになる感じです。

read barrierとかwrite barrierといった細かい話もあるのですが、思い切ってカットしています。 理解が難しい上に、アルゴリズムをざっくり理解するには詳細すぎる情報だったからです。

できるだけ簡単に多くの人に聞いていただきたかったので、「GCの知識がほんの少し必要です」ということでTwtterで宣伝してました。ですが、G1 GC/GMS GCを理解していないと、20分で理解するのは難しかったと思います。

発表の感想

VGAのコネクタをボランティアスタッフの方に貸していただけたのは大変助かりました。ありがとうございました。

発表自体は時間をオーバーすることもなく発表できました。EpsilonGCで最後に落として少し笑ってもらえたので満足しています。

150名程度の部屋でしたが、だいたい埋まっていたので興味がある人には情報を届けられたのではないかと思います。 ネットやアンケートの情報を見る限り、満足いただけた方も多かったのではないかと思います。

次発表するぞとおもっている人へ

カンファレンスはすごい事例ばかり出てくるので物怖じするかもしれません。Java関連で自分が面白いと思っている技術があったら、ぜひ応募してみましょう。登壇準備は大変ですが、技術を理解するには効果的です。

失敗談を発表しても登壇者にとって振り返りになりますし、同じようなことで悩んでいる人の参考になります。

あまりにアレだったら、CFPで落選すると思いますし、あまり心配しすぎずに応募してみましょう。

私みたいに、自由研究の発表でも大丈夫です。コミュニティが盛り上がっているというのはすごい大事です。

なので、自分のおかげでコミュニティが盛り上がるんだくらいの大船に乗った気持ちで、発表しましょう。

ネット上で見つけた質問とか

128TBのメモリの意味がわかりません

ZGCの場合、0bitから42bitを使ってヒープ上のメモリの場所を表現しています。このため、4TB分のヒープが理論上の最大値になります。

ZGCの場合、43bit-64bitもカラーポインタを表現するのに使ったり、オブジェクトの情報を保存する領域として予約されているようです。そのため、64bitアドレスをすべて使うことになります。64bitすべてのアドレスをすべて使うと64bitで表現できるメモリ容量は128TBということになります。(64bitすべてを使うと16EBで、これは私の理解が間違っていました。)

Shenandoah GC、ZGCとG1GCと何が違うの?

コンパクションGCのときにアプリケーションスレッドを動作させながら行うところが一番違います。 G1GCの場合、young GCとmixed GCのときにコンパクションGCを行うためアプリケーションスレッドが停止します。

G1GCは、目標停止時間を設定してそれを元にGCスレッドを動作させますがSheandoah GC/ZGCはマーキングフェーズの開始と 最後の確定時くらいです。コンパクションGCにともなうアプリケーションスレッドの停止はありません。

最後に

JJUGの会場ボランティアスタッフのみなさま、スポンサー企業様のおかげで、非常に円滑にJJUG CCC 2018 Springが行えたと思います。ありがとうございました!

会場も広く、セッション盛りだくさんで、楽しいカンファレンスになりました。

さて、秋は何をしゃべるかねぇ

Javaのセキュリティ関連の設定を上書きしたい

TL;DR

  • Javaのセキュリティ関連の設定が /etc/java-8-openjdk/security/java.policy などに有る場合がある
  • 実行時に-Djava.security.properties=<URL> を渡すとマスターファイルから特定のプロパティを上書きできる

Javaのセキュリティ管理の設定ファイルで java.policy というファイルがある。

特定の値を上書きしたいケースがある。

例えば networkaddress.cache.ttlnetworkaddress.cache.negative.ttl のキャッシュの設定を上書きしたい

/etc/java-8-openjdk/ 下の設定ファイルは debパッケージのインストールなどで上書きされるので、できるだけ /etc/java-8-openjdk/security/java.policy は編集したくない。

調べたところ、 実行時に-Djava.security.properties=<URL>/etc/java-8-openjdk 下の設定ファイルの内容を特定のプロパティのみ上書きできる。

class Main {
  public static void main(String args[]){
    System.out.println(java.security.Security.getProperty("networkaddress.cache.ttl"));
    System.out.println(java.security.Security.getProperty("networkaddress.cache.negative.ttl"));
  }
}

java.securityはpropertiesファイル形式

networkaddress.cache.ttl = 1
networkaddress.cache.nagative.ttl = 1

javacでコンパイル後、次のように確認できる

$ java Main
null
10
java -Djava.security.properties=./java.security Main  
1
1

Facebook OAuth redirect URI の制限モードについて

2017年12月にfacebookから Strict Redirect URI Matching なる警告が来ていた.

TL;DR

  • OAuth認証の時にredirect_uri に設定できるURIホワイトリスト管理されるようになる
  • URIの完全一致しか許さない制限モードが3月に有効化される
  • facebookの仕様どうりにURIパラメーターを付与しないとエラーになる

制限モードとは?

facebook developer site > 制限モードの有効化 から引用

制限モードのしくみ

制限モードでは、有効なOAuthリダイレクトURIリストとの完全一致を条件にすることによって、
リダイレクトURIのハイジャックを防ぎます。たとえば、リストにwww.example.comが含まれている場合、
制限モードではwww.example.com/tokenは許可されません。有効なOAuthリダイレクトURIリストに
存在しない余分なクエリパラメーターを含めることもできません。

制限モードが有効化された後はOAuth認証時にOAuthのリクエストを実行するときに redirect_uri を指定するが、これにパラメーターを付与することができなくなる。

redirect_uri にパラメータを付与しているプログラムがありOAuth認証のリクエスト側に対応が必要になった。

stateパラメータを利用する

redirect_uri は完全一致になるが、state パラメータが用意されているので、それを利用する

curl https://www.facebook.com/v2.11/dialog/oauth?
  client_id=1234567890
  &redirect_uri=http://example.com/facebook/loginCallBack
  &state=cgalw3fZDBjemdCtByb6NdMmYWxk9bkV

リダイレクトURLは次のようになる

http://example.com/facebook/loginCallBack?
  state=cgalw3fZDBjemdCtByb6NdMmYWxk9bkV

facebook側の設定

facebookの設定では、「有効なOAuthリダイレクトURI」を設定した後に、「リダイレクトURIに制限モードを使用」を有効にすると、redirect_uri が完全一致チェックが行われるようになる

f:id:yokotaso:20180111131440p:plain