その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

命名の話

命名の話をしました.1時間位でなんとか間に合わせで書いた資料です.
よろしくお願いします.

www.slideshare.net


あと,例えばメソッド名で"get"と"retrieve"と"fetch"をどう使い分けるか,みたいな話題が出て,
僕あたりは

  • getは単純なゲッター的なものに使う
  • retrieve は DB にアクセスして値を取ってくるものに使う
  • fetch は何らかごちゃごちゃ処理をして (API 叩くとか) 所望の値を取ってくるものに使う

という風な使い分けの話をしました.そういったルールが各々の人の中にはあります.
ここらへんのルールというのは個人が好き放題やっても無力なので (というかノイズたりえるので) プロジェクトの人間たちが話し合ってすり合わせる必要があります.
とは言え,いちいち些細な命名の問題について話し合っていくのはだるいので日常的なコードレビュー等でコモンセンスというか文化みたいなものを育てていくのが重要だと思っています.

人間には名前を考える能力というものがあるので積極的に考えていくと良いでしょう.考えると得です.

こちらからは以上です.


以上と言いましたがその後「変数名に ? とか ! とか使えると便利だよね〜」という話があって,そう言えばそうですね,便利です,という認識を改にしました.is とか付けるよりもスマートですよね.
Ruby とか LISP とかはここらへん使えて大層良いのですが,なかなかそういった命名を許している言語は多くないですね.ウムー

MySQL の lower_case_table_names について,テーブル名とデータベース名の中の大文字小文字について

MySQL には lower_case_table_names という変数があって,これはテーブル名の大文字小文字を区別するかどうかというのを設定するパラメータなわけですが,その詳細は以下のようになっています (参照: MySQL :: MySQL 5.6 Reference Manual :: 9.2.2 Identifier Case Sensitivity).

Value 説明
0 大文字・小文字を区別してデータベース名やテーブル名をストアする.これらの名前の比較についても大文字・小文字を区別する.MySQL のサーバが動作する OS のファイルシステムが大文字と小文字を区別しない場合 (例えば OS XWindows),この値に設定すべきではない.そうしたファイルシステム上でこの値を0にして,なおかつ MyISAM のテーブル名に対して大文字小文字を混ぜてアクセスするとインデックスがぶっ壊れることがある.
1 テーブル名やデータベース名をストアするときにすべてを小文字に丸める.名前の比較については大文字・小文字を区別しない.
2 テーブル名やデータベース名をストアするときはそのままストアする (つまり大文字・小文字情報を保持したままストアする).Lookup 時はこれらの名前が小文字に変換され,名前の比較については大文字・小文字を区別せずに行う.なおこの機能は,大文字・小文字を区別しないファイルシステムでのみ機能するInnoDB のテーブル名は lower_case_table_names=1 に設定した時のようにすべて小文字で保存される.

手元でデフォルト値を調べてみたら,CentOS に入っている MySQLlower_case_table_names は 0,OS X では 2,更に Windows では 1という風になっていてマジカオス!! という感じで *1,実際に Windows 上で mysqldump で吐いた dump を LinuxMySQL に食わせたら "duplicated key" みたいな感じで死ぬ,みたいな事が起こりました.


これを避けるためには単一のプラットフォーム上で開発すれば良いわけですが,皆さんにおかれましては OS X で開発して Linux のプロダクション環境にデプロイするみたいな事も多いでしょうし,Windows での開発を貫く開発メンバーも一定数存在していることと存じます.


というわけで対策をする必要があります.公式のドキュメントにも載っていますが,取るべき方法は3つあります.

1. lower_case_table_names=1 をすべてのシステムで利用する.SHOW TABLES や SHOW DATABASE を叩いた時に,もとの大文字・小文字が区別された名前を見れないという欠点がある.
2. OS XWindows では lower_case_table_names=2 を使用する.その他のシステムでは0を使用する.SHOW TABLES や SHOW DATABASE を叩いた時でも大文字と小文字が区別された名前を確認することが出来る.一方で OS XWindows 上で,ユーザのステートメントがデータベース名やテーブル名をひくときに大文字と小文字が正確に区別されているかを確認する必要がある.例外として,InnoDB を使っていてなおかつこれらの問題を回避したい場合は,(この方法を使うよりも) すべてのプラットフォームで lower_case_table_names=1 を強制したほうが良い.
3. データベース名やテーブル名には常に小文字を使う.


3 の「データベース名やテーブル名には常に小文字を使う」というのがまあてっとり早くて良いと思います *2
それに加えて1の方法を採って,lower_case_table_names=1 を設定し,小文字の利用をシステム側で強制しておくと事故が起こりにくくなって良いのではないかと思った次第です.


ちなみにlower_case_table_names=1 にしたい時は古いデータベースをコンバートする必要があるので注意.行動が必要になった際はドキュメントを読みましょう.
https://dev.mysql.com/doc/refman/5.6/en/identifier-case-sensitivity.html

結論

とにかくデータベース名やテーブル名には大文字と小文字を混ぜない.小文字だけでやっていく,という意識が重要そう.併せて lower_case_table_names=1 を設定する.

あと,根本的な話として開発環境と本番環境はできるだけ近づける必要がある.WindowsMac で開発して Linux の本番環境にデプロイとか,その逆とかをしていると MySQL に限らず問題が起こる可能性が高まるので何とかしましょう.現存するソリューションとしては仮想マシンなどがあります.

その他

  • InnoDB だったら,大文字小文字を区別しないファイルシステムであっても lower_case_table_names=0 を使っても問題ないのだろうか?
  • 大文字小文字を区別するファイルシステムlower_case_table_names=2 にした時は,lower_case_table_names=0 に fall back される
  • 一部のドキュメントの意味がよくわからなかった……






*1:とは言えこれは仕方がない

*2:とはいえ色々あってそうも出来ないこともある

donovan という1枚 java ファイルを書けばウェブアプリを立ち上げられる WAF を書いた

※ WAF: Web Application Framework


表題の通りです.Maven Central にも上がっています.


avans という tokuhirom さんが書いた WAF があり,Sledge や Amon2 の流れをくむシンプルで良いフレームワークがあって,最近はもっぱらそれでウェブアプリケーションを書く日々が続いているわけですが,
avans でウェブアプリ―ケーションを立ち上げるには class を真面目に定義したり設定ファイルをちゃんと書いてやる必要があり,もう少しゆるふわ,というか雑にやりたいな〜という欲求が生まれる瞬間があります.

そうした思いから,この donovan という WAF を作成しました.
ご多分に漏れず,avans の影響をバリバリ受けているというか,コードの多くを参考にしています.


簡単に説明すると

  • 組み込みの Servlet Container (Embedded Tomcat or Embedded Jetty) により動作する
  • ディスパッチャとそのパスの動作を lambda によって一緒に書ける
  • java のファイルを1枚書けば動く
  • 割と依存が少ない

という特徴を持ったミニマルな WAF になります.


donovan で Embedded Jetty を使った例を書くと,

@Slf4j
public class App {
    public static void main(String[] args) throws Exception {
        DonovanJetty dj = new DonovanJetty()) 
        dj.get("/", (c) -> {
            return c.renderJSON(new BasicAPIResponse(200, "hello!"));
        });

        dj.start();
        log.info("{}", dj.getURL());
        dj.join();
    }
}

と書いて起動してやると*1,「ルートを GET で叩くと "hello!" が返ってくるというようなアプリケーション」をザクッと書くことが可能となります.
各エンドポイントは donovan からコンテキストが渡され (色々な情報が入っている),WebResponse (webscrew が提供する interface) を返すという処理を実装すれば良いという感じです.
詳細は README.mdjavadoc をご覧ください.


こうした感じで,donovan を使うとゆるふわにウェブアプリをガツンと立ち上げることが可能となります.
主に API のモックや,ちょっと動作を確認したいときなんかに使う為に作成した WAF なので本チャンの環境で使うようなものではございませんのでやめたほうが良いでしょう *2.まあ小さいものならいけそうではある *3

ご利用くださいませ.

*1:djという変数名が良いですね

*2:というかこれでちょっとでもデカいもの書こうとすると設計が破綻する気がする

*3:spark なんかは似ている思想っぽいし……まあとはいえ組み込み servlet container ではないので微妙なところ http://sparkjava.com/

Java でパターンに基いてランダムな文字列を生成できる random-string というのを書いた

Java 版の random-string を書いたので Maven Central にアップしました.


これはパターンに基いたランダムな文字列を生成する君です.
他言語の実装には PerlString::RandomJavaScriptString_random.js などがあります.
この Java 実装は Perl 版の移植になります *1


使い方としては以下のような感じ.

パターンを使う方法
RandomStringGenerator generator = new RandomStringGenerator();
String randomString = generator.generateFromPattern("cCn!.sb"); // (例えば "aB4@X.Ç" が得られる)
正規表現を使う方法
RandomStringGenerator generator = new RandomStringGenerator();
String randomString = generator.generateByRegex("\\w+\\d*\\s[0-9]{0,3}X"); // (例えば "a5B123 18X" が得られる)


詳しい事は javadoc 等を参照して頂ければと思います.
以上です.よろしくお願いします.


[追記]

セキュアな文字列を生成するわけでは無いので,そういう用途の時は SecureRandom をお使い下さい!!!!!

[追記2]

バグフィックスも兼ねて 1.1.0 を出しました.
一番の変更は,RandomStringGenerator のコンストラクタに Random Class のインスタンスを渡せるようにしたことです.マルチスレッドの兼ね合いなんかで ThreadLocalRandom とかを使いたいこともあると思うので……

実用としては,RandomStringGenerator のコンストラクタには任意の Random Class のインスタンスを渡す事をおすすめします.

*1:まだ入れてない機能 (パターンを自分で定義できる機能とか) がいくつかありますが……

git で管理しているリポジトリの各ブランチの中身をそれぞれ個別のディレクトリにエクスポートする

人生にはこういう事が度々あります.深く考えないようにすることにします.


その結果できたのがこれです.

リポジトリのディレクトリ以下でこのコマンドを叩くとイナフという感じです.


Enjoy!!

Servlet コンテナによって HttpServletRequest#getPart(String name) の挙動が違う

Part gotPart = httpServletRequest.getPart("hoge");

みたいなコードがあった時,送られてきた multipart リクエスト (httpServletRequest) に1つも part が含まれていない場合の挙動が Servlet コンテナによって変わってきます.

送るリクエストはこんな感じ.

HttpPost post = new HttpPost("http://localhost:8080/");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
HttpEntity entity = builder.build(); // part は空のまま instantiate
post.setEntity(entity);
HttpClient client = HttpClientBuilder.create().build();
HttpResponse response = client.execute(post);

この場合に Tomcat 8.0.20 でこのコードを実行すると,gotPart には null が入ってきますが,
Jetty 9.2.9.v20150224 で実行すると,getPart("hoge") の実行によって IOException が上げられます.
Jetty の実装を見るとリクエストに form-data の行が無かった場合に IOException を上げるようになっているようです.
https://github.com/eclipse/jetty.project/blob/a3201a3c810da37ad892e62c1f04600dc889d14a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java#L490-L491


Jetty が IOException を上げてくるのが良いかどうかはさておき,ここらへん細かい差異があるので実装にあたって注意が必要ですねという印象です.

textsearch_sennaをキレイにアンインストールする

textsearch_sennaというのがあって,これはsennaを使った全文検索postgresqlに提供するために便利な演算子や関数を提供してくれるやつなんですが,これをアンインストールする必要が出てきた時に「インストール時に追加された関数とか演算子とかビューとか全部調べて手で削除しないとマズいのかーうへー」とか思ってたんですけど,そんなことする必要無かったのでメモ.


結論からいうと,$PG_HOME/share/contrib/uninstall_textsearch_senna.sqlを流し込んでやるとよろしい.このファイルはtextsearch_sennaインストール時に同時に提供されるファイルです.

psql --user=user_name -f $PG_HOME/share/contrib/uninstall_textsearch_senna.sql db_name

これで関連のものがDBから削除されるので,あとはtextsearch_sennaをインストールする時に使ったMakefileを利用して

PATH=$PG_HOME/bin/:$PATH make uninstall

などとしてやると完全にアンインストールされます.ちゃんとアンインストールの為のユーティリティが用意されていた!


ちなみになぜキレイに消し去らなければならないのかというと,pg_updateでpgサーバのバージョンを上げるとなった時に,新しいバージョンではtextsearch_sennaは必要無いのにも関わらず「古いバージョンがtextsearch_senna要求してるけど新しいバージョンにはtextsearch_sennaが入ってないよ〜」とか怒られて死ぬのでそれを回避する為です.
世の中には色々な理由があります.理由があるのです.