« 2011年3月 | トップページ | 2011年6月 »

2011年5月の14件の投稿

2011/05/22

Invalid pixel aspect ratio問題に対応したさきゅばす/coroid用ffmpeg for WIN

 

ffmpeg.exeをダウンロードする(ミラー)

libav(ffmpeg.exe)をダウンロードする(ミラー)

 

先日のエントリ

でffmpeg公式のソースに表題の問題対応が入っていることを記載しました。その後、バグレポートを更新したところ、libavの方にも修正が入ったようです。

そして、本日、ffmpegのリポジトリからソースをとってきてコンパイルしたところ、見事問題が解消されていることを確認しました。その他にも、再配布可能なライセンス内での音質もかなり向上したり、x264のオプションが以前よりシビアでなくなったり、良いことづくめでした。また、ffmpegのパラメータをそれなりに調整すればマルチスレッドで動作するようなので、変換も早くなると思います。

本エントリ冒頭のリンクからダウンロードできますので、さきゅばす1.4.4、coroid-server0.5.3共にbinディレクトリの中にあるffmpeg.exeをこれで置換してください(一応オリジナルはバックアップ取っておいた方が良いかもしれませんが)。

不具合があればご連絡お願いします。あと暇があればffmpegとlibavどちらが良いかも教えて頂けると。

 

linux版は、環境が無いのでビルドできませんでした…元々Ubuntuの上の仮想環境でCentOSを動かして、そこでビルドしていたのですが、その環境をつくる空き容量が無く。

 

さきゅばす/coroid-server用のffmpegソースコードはSourceForgeのGitリポジトリにおいています。libavはこちらです。

2011/05/19

クラスのロードとイニシャライズはアトミックではない

と記述してきましたが、とっくの昔に説明されている方がいらっしゃいました。

こちらのエントリの中で、Class.forNameメソッドでstatic initializerが働くのが当たり前、というふうに書かれているのですが、理由がわかりませんでした(使ったことないし…)。

が、Javadocを見れば一目瞭然でした。 Class.forName(String, boolean, ClassLoader) のパラメータの説明を見ると

initialize - クラスを初期化する必要の有無

とありました( Class.forName(String) の場合はtrue、つまり初期化有りです)。

このようにシンプルな説明ができるようになったところでこの話を終わります。

2011/05/18

mainメソッドの無いクラスを実行した際にスタティックイニシャライザが呼ばれるのは仕様では無かった

昨日のエントリ

で、mainメソッドが無いクラスをjava.exeで呼んだ時の挙動がときの挙動がJava6とJava7で変わっている、と書きました。また、最後に

だからなんだ、と言われると困りますが。

というように書いたのですが、その後この一言が何故か頭から離れませんでした。

 

モヤモヤを解消するため考えを整理することにしました。私は昨日の時点では以下のような認識でした。

  • static initializerは、クラスがロードされた時点で実行される 。
    • java.exe Starter というコマンドを実行した時点では、 本当にStarterクラスにmainメソッドが含まれているかどうかわからない。
    • 従って結果がどうであれStarterクラスをロードする必要がある。
    • 実際にはmainメソッドが含まれていなかったとわかる(NoSuchMethodError)のは、ロード後、すなわちstatic initializerが実行された後である。

しかし、上記のような仕様であるなら、Java7でも同じ挙動でなければならないはずです。この点が引っ掛かりを感じた原因でした。

そこで、実装(OpenJDK7 b142, OpenJDK6 b22)と仕様(The Java Language Specification, Third Edition)それぞれで確認してみました。

 

実装の部

OpenJDK7では、Mainクラスのロードと、そのクラスにmainメソッドが含まれているかのチェックをsun.launcher.LauncherHelperクラス(Enumでのシングルトンパターン実装ですね)のcheckAndLoadMainメソッド及びgetMainMethodメソッドで行っていました。以下に抜粋します(OpenJDK7 b142のソースとは同一ではないのですが意味合い的には変わらないため、上のリンク先から抜粋しています)。

   1: ClassLoader loader = ClassLoader.getSystemClassLoader();
   2: clazz = loader.loadClass(classname);
   3:  
   4: try {
   5:     method = clazz.getMethod("main", String[].class);
   6: } catch (NoSuchMethodException nsme) {
   7:     ostream.println(getLocalizedMessage("java.launcher.cls.error4",
   8:             classname));
   9:     throw new RuntimeException("Main method not found in " + classname);
  10: }

前述した私の理解が正しいのであれば、2行目でクラスロードしたときにstatic initializerが働き文字列が出力されるはずなのですが、実際のJava7はそのような動作になっていません。

あれ?と思い、同じようにloadClassメソッドを使って昨日のStarterクラスをロードしてみました。

   1: public class Main {
   2:     public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException,
   3:             IllegalArgumentException, InvocationTargetException {
   4:         ClassLoader ld = ClassLoader.getSystemClassLoader();
   5:         Class<?> c = ld.loadClass("Starter");
   6:         try {
   7:             Method method = c.getMethod("main", String[].class);
   8:             method.invoke(c, new String[]{});
   9:         } catch (NoSuchMethodException ex) {
  10:             System.err.println("NoSuchMethodException");
  11:         }
  12:     }
  13: }

…やはり出力されません(Java6/7とも)。じゃあなぜJava6のjava.exeでは出力されているのかが気になります。

Java6で該当する処理は、java.c内のJLI_Launch関数内で実装されていました(JNIでの実装です)。こちらも大きく省略した抜粋コードを以下に示します。

   1: int
   2: JLI_Launch()
   3: {
   4:     mainClass = LoadClass(env, classname);
   5:     /* Get the application's main method */
   6:     mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
   7:                                        "([Ljava/lang/String;)V");
   8: }
   9:  
  10: static jclass
  11: LoadClass(JNIEnv *env, char *name)
  12: {
  13:     cls = (*env)->FindClass(env, buf);
  14:     return cls;
  15: }

JNIのFindClass関数でMainクラスを取得し、同じくJNIのGetStaicMethodID関数でmainメソッドを取得しています。こちらの方法でも昨日のStarterクラスをロードしてみます。

   1: #include <jni.h>
   2: #include <stdio.h>
   3: #include <stdlib.h>
   4:  
   5: int main(int argc, char **argv) {
   6:     JNIEnv *env;
   7:     JavaVM *jvm;
   8:     JNIEnv jni;
   9:     JavaVM vmi;
  10:     JavaVMInitArgs vm_args;
  11:     JavaVMOption options[4];
  12:     /* 省略 */
  13:  
  14:     JNI_CreateJavaVM(&jvm, (void **) & env, &vm_args);
  15:     jni = *env;
  16:     vmi = *jvm;
  17:  
  18:    jclass cls = jni->FindClass(env, "Starter");
  19:    if (cls == 0) {
  20:        printf("Starter Not Found.\n");
  21:        exit(1);
  22:    }
  23: /*
  24:     jmethodID mid = jni->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");
  25:     if (mid == 0) {
  26:         printf("main method Not Found.\n");
  27:         exit(1);
  28:     }
  29: */
  30:     vmi->DestroyJavaVM(jvm);
  31: }

これを実行してみると…文字列が出力されました!

FindClass関数でクラスをロードするとstatic initializerが働くようです。ただ、ドキュメントにはそういったことは書かれていないように見え、期待される動作なのかはわかりませんでした。(補足すると、この部分はJava6で試しました。Cコンパイラがある環境にJava7を導入していないもので…)

 

そんなわけで、昨日気付いたJava6とJava7の挙動の違いは、Bootstrap実装の差異によるもののようです。

 

仕様の部

仕様書ではどう書かれているのでしょうか。そのままのタイトル 12.4.1 When Initialization Occurs という節がありました。引用します。

A class or interface type T will be initialized immediately before the first occurrence of any one of the following:

  • T is a class and an instance of T is created.
  • T is a class and a static method declared by T is invoked.
  • A static field declared by T is assigned.
  • A static field declared by T is used and the field is not a constant variable (§4.12.4).
  • T is a top-level class, and an assert statement (§14.10) lexically nested within T is executed.
Invocation of certain reflective methods in class Class and in package java.lang.reflect also causes class or interface initialization. A class or interface will not be initialized under any other circumstance.

この記述を読むと、昨日のStarterクラスでの実例のように、宣言していないstaticなmainメソッドを呼ぼうとしたときは初期化されるべき条件には当てはまらないことが分かります。また、 12.4.2 Detailed Initialization Procedure には、Classオブジェクトの状態として

This Class object is verified and prepared but not initialized.

という状態があることも書かれており、おそらくこの状態なのではないだろうかと思います(注:かなり端折って読んだので勘違いの可能性もあります)。

 

というわけで、冒頭に記載した私の認識は誤りである、つまり

  • Classの初期化はクラスロード時に行われるわけではない

ということことがわかりました。そして、mainメソッドの無いクラスを指定して実行した場合にstatic initializerが実行されていないJava7の挙動が仕様から外れているわけでもなさそうです。(will notということは、上記条件に当てはまらない状況ではinitializeされない方が普通、むしろJava6の挙動がおかしい、ってことですよね?)

2011/05/17

『mainメソッドの作成なしでのプログラムの実行』はJava7では動かなかった

 

で説明されているタイトルのコード

public class Starter {
static {
System.out.println("Hello!");
}
}

を見て私も思いつきました。

public enum Starter {
私の, 名前は, 中野です;
private Starter(){
System.out.println(toString());
}
}

 

しかしどちらも、Java6では

> java Starter
私の
名前は
中野です
Exception in thread "main" java.lang.NoSuchMethodError: main

のように文字列は表示されるのですが、Java7で実行しようとすると

> java Starter
エラー: メイン・メソッドがクラスStarterで見つかりません。次のようにメイン・メソッドを定義してください。
   public static void main(String[] args)

と、スタティックイニシャライザも働いてくれないようです。だからなんだ、と言われると困りますが。[追記: mainが無くてもスタティックイニシャライザが走るJava6の方がおかしかったみたいです…]

2011/05/15

Firefox3ではあったメニューがFirefox4でなくなっている

マウスのサイドボタンを再び使えるようにしたので、「閉じる」機能を割り当てたらFirefox4ではタブでなくウィンドウが閉じる状態になってしまいました。結局「閉じる」ではなく「Ctrl+W」を割り当てることでこの問題を回避しました。

そんな問題にあたっている最中、ふとFirefox4のメニューを見てみると、「タブを閉じる(Ctrl+W)」メニューが無くなっていることに気付きました。

検索したところこれは仕様だそうで、他にもいくつか無くなっているものがあるらしいです。

これらのメニューをFirefox4でも表示させるには、

C:\Users\[user_name]\AppData\Roaming\Mozilla\Firefox\Profiles\[xxxx].default\chrome\userChrome.css

のファイル([]内の文字列は環境によって異なります)に次の文字列を追加するとよいです。

@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); /* only needed once */

/* Always show all hidden menu entries */
#navigator-toolbox menuitem[class="show-only-for-keyboard"] { display:-moz-box !important; }

しかしメニューが隠されてしまうと、対応するショートカットが何か分からなくなるような。Ctrl+Wくらいなら他のアプリの動作から推測可能ですが。

参考:

libxvid: Invalid pixel aspect ratio の件が修正されているかもしれない

ffmpegプロジェクトがffmpegとlibavに分裂した、という話は聞いていたのですが、当時ffmpegを全く触っていなかったので聞き流していました。

そして久しぶりにさきゅばす用ffmpegをビルドしてみようと、公式リポジトリからソースをとってこようとした際に、ふと、ffmpeglibavどちらを参照すれば良いのだろう、という疑問がわきました。

という流れで、

のつづきです。

以前記載したバグレポートのURLは変更になっており、 https://roundup.libav.org/issue1931 に変わっていました。このレポートの動きはありません。

しかし、実際にffmpegのリポジトリからソースをとってきてさきゅばす用のパッチを当てようとするとrejectされてしまいました。どうも差分周辺のソースが変更されたようです。そこで、その行が変更されたコミットを調べるため

$ git log -S'if (ost->picref->video && !ost->frame_aspect_ratio)' -- ffmpeg.c

コマンドを実行してみると、以下のように出力されました。

commit abf8342aa94bdf06bb324f6723a6743dd628d5c6
Author: Baptiste Coudurier <baptiste.coudurier@gmail.com>
Date:   Sat Apr 16 16:47:24 2011 -0700
   ffmpeg: fix -aspect cli option
   Redesign the way -aspect option is handled. This is done by making
   ffmpeg read the sample aspect ratio set in the corresponding input
   stream by default, and overriding it using the value specified by
   -aspect.
   If the output display aspect ratio is specified with -aspect, it is
   set at the end of the filterchain, thus overriding the value set by
   filters in the filterchain.
   This implementation is more robust, since does not modify the
   filterchain description (which was creating potential syntax errors).

やはり-aspectオプションの不具合に対する変更のようです。ちなみに http://git.videolan.org/?p=ffmpeg.git;h=abf8342aa94bdf06bb324f6723a6743dd628d5c6 でオリジナルを参照できます。

そんなわけで、この不具合に対するパッチは必要なくなったのでは、と期待しています。…実際のビルドは来週以降になりますのでまだ試せていないのですが。

libavの方は上記の対応は取りこまれていませんでした。

そんなわけで、私がついていくプロジェクトは決まったような気がします。

2011/05/14

CPU切替器経由で接続するとマウスのサイドボタンを認識しなくなってしまった

以前はREX-220CXDというCPU切替器でPS/2キーボードとMicrosoft Laser Mouse 6000を共用していたのですが、1年ほど前にUSB接続のキーボードMicrosoft Natural Ergonomic Keyboard 4000というUSB接続のキーボードを購入したのをきっかけに、CPU切替器もKVM-DVDHU2というものに買い替えました。

当初はこの組み合わせで問題なく動作していたのですが、いつからかマウスの右クリック、左クリック、スクロール以外のボタンが効かなくなってしまいました。

当時のことを思い返したところ、IntelliPointを8.0にアップデートしたことに気付きました。そこでIntelliPointのヘルプを読んでみたところ、ボタンが効かなくなるのであれば前のバージョンに戻せ、という旨の記述がありました。

それもまた面倒なので(というか前のバージョンのIntelliPointが見つからない)どうにかならないか、とマニュアル(PDF)を見てみたところ、「マウスエミュレーションを無効にする」の項目に「多機能マウスを接続している場合で、一部の機能が使用できないときは、…」という記述が見つかりました。さっそくこれを試したところ、無事サイドボタンも使えるようになりました。

ただ、マウスエミュレーションを無効にすると、休止モードから復帰してからマウスが反応するまでに時間がかかるようになるんですよね…前のCPU切替器と比べたときの優位点が一つ減ってしまいました。

JOINが遅すぎて泣ける ― MySQLは漢(オトコ)のコンピュータ道?

はじめての永続化、全2話中第2話、最終回です。第1話はこちら。サブタイトルに意味は無いです。

下のような関連を持つありがち、とはいかないまでも珍しくは無いクラス群をクエリしようと思いました。(クリックで拡大)

mysql_uml

Earthにいくつかのマスタデータがくっついているようなイメージです。ちなみにクラス名は適当につけているので意味はありません。

今回記載しているのはMySQL5.5です。MySQL5.1でも試しましたが、パフォーマンスは同程度でした(ただし後で述べますがEXPLAINの結果は若干変わっていました)。その他テストに使用したものは以下の通りです。

クエリはこんな感じです。これを実行すると、上で添付したSQLと、select-joinのクエリがいくつか走ります。

   1: Criteria c = session.createCriteria(Earth.class);
   2: List<Earth> res = c.list();

結果は6件得られます。

問題は実行にかかる時間なのですが、この程度のデータ数で1分程度かかってしまいました。上で添付しているメインのSQLをEXPLAINした結果の抜粋は下記の通りです(結果の完全なものは上に添付しています)。IDと他の関連先テーブルのIDから成る外部キーしか持っていないので当然ですが、”key”は全てPRIMARYになっています。

   1: +----+-------------+-----------------+--------+---------+--------------------------------+
   2: | id | select_type | table           | type   | key     | Extra                          |
   3: +----+-------------+-----------------+--------+---------+--------------------------------+
   4: |  1 | SIMPLE      | beta2_          | index  | PRIMARY | Using index                    |
   5: |  1 | SIMPLE      | beta4_          | index  | PRIMARY | Using index; Using join buffer |
   6: |  1 | SIMPLE      | beta2_1_        | eq_ref | PRIMARY |                                |
   7: |  1 | SIMPLE      | beta4_1_        | eq_ref | PRIMARY |                                |
   8: |  1 | SIMPLE      | this_           | ALL    | NULL    | Using where; Using join buffer |
   9: |  1 | SIMPLE      | shot15_         | eq_ref | PRIMARY |                                |
  10: |  1 | SIMPLE      | block5_         | eq_ref | PRIMARY | Using index                    |
  11: |  1 | SIMPLE      | block5_1_       | eq_ref | PRIMARY |                                |
  12: |  1 | SIMPLE      | departure6_     | eq_ref | PRIMARY | Using index                    |
  13: |  1 | SIMPLE      | direct7_        | eq_ref | PRIMARY |                                |
  14: |  1 | SIMPLE      | problem8_       | eq_ref | PRIMARY |                                |
  15: |  1 | SIMPLE      | problem9_       | eq_ref | PRIMARY |                                |
  16: |  1 | SIMPLE      | arm10_          | eq_ref | PRIMARY |                                |
  17: |  1 | SIMPLE      | computer11_     | eq_ref | PRIMARY |                                |
  18: |  1 | SIMPLE      | curry12_        | eq_ref | PRIMARY |                                |
  19: |  1 | SIMPLE      | exhibition13_   | eq_ref | PRIMARY | Using index                    |
  20: |  1 | SIMPLE      | exhibition13_1_ | eq_ref | PRIMARY |                                |
  21: |  1 | SIMPLE      | radio14_        | eq_ref | PRIMARY |                                |
  22: |  1 | SIMPLE      | piano3_         | eq_ref | PRIMARY |                                |
  23: |  1 | SIMPLE      | piano3_1_       | eq_ref | PRIMARY | Using index                    |
  24: |  1 | SIMPLE      | piano3_2_       | eq_ref | PRIMARY | Using index                    |
  25: |  1 | SIMPLE      | departure6_1_   | eq_ref | PRIMARY |                                |
  26: +----+-------------+-----------------+--------+---------+--------------------------------+
  27: 22 rows in set (56.69 sec)

にも関わらず、遅い。たったこれだけのデータ量に対してクエリするだけで私の環境で1分程度かかります。

PROFILINGの結果を見ると、statisticsという部分で時間のほとんどが費やされています。ソースコードをチラ見してみると下のようなコメントがあり、やはりJOINに関わるところなんだろうな、と推測できます。

   1: /* Calculate how to do the join */
   2: thd_proc_info(thd, "statistics");
   3: if (make_join_statistics(this, select_lex->leaf_tables, conds, &keyuse) ||
   4:     thd->is_fatal_error)
   5: {

コンフィグをデフォルトのまま使っているのが問題なのでは、と、書籍を参照したりwebを検索したりして目ぼしい設定を変更してみたのですが改善されず。というか、私の感じたところ、コンフィグ設定というのは(RDBMSにとって潤沢とは言えない)メモリ資源をどこに分配するのかを決定するものなのかなと。一方、今回の件はタスクマネージャーを見てる限りmysqld.exeが25%に張り付いており(4コアなので)、メモリではなくCPUバウンドな問題のように思われます。従ってあまりコンフィグ設定は結果に影響を与えるものではないと結論付け、諦めました。

上のSQL文から、Shotテーブルに属すカラムを削除し、それに伴って不要となったShotテーブルのJOINも削除することで、大分速度は改善されました。それでも6秒程度はかかっていますが。

同じデータに対して同じクエリをPostgreSQL9.0とOracle10g XEで試したところ、どちらも即答でした。

素人考えだと、同じ結果を得られるのであればSELECT文は少ない方が速い、と考えていたのですが(少なくともMySQLにおいては)そうではない状況もあるのだと気付かされたのも新鮮でした。

実際のエンティティクラスはもちろん他の属性も持っていますし関連もこれより多く、更にMySQLが苦手であるということで有名な相関サブクエリなんかも行っていますので私はPostgreSQLに逃げることにしました(あとMySQLではJOINの最大数が61というのも心許ない)が、もしMySQLを使用するのであれば、JOINを控えめにした方が良さそうです(Lazy Fetchにするとか、Select-Fetchにするとか、何らかの対価で回避できる状況はあると思います)。ただし、前回記載したような強制的にJoin-Fetchになるような場合もある(HHH-3538)のでFetch戦略を自分で制御できない部分があるかどうかは注意して見ておくべきでしょう(自分は一回目のクエリはIDにProjectionして2回目のクエリでそのIDをキーに引っ張ってくる、という自力Select-Fetchみたいなこともやってました)。

本筋から外れますが最後に。上で示したEXPLAIN結果の5行目Extra列ではUsing indexとUsing join bufferが同時に表れています。他の方が書かれたMySQLパフォーマンスチューニングに関するいくつかのblogエントリでは『join bufferが使用されるのはindexが用いられない場合のみであり、コンフィグのjoin_buffer_sizeを増加させるよりindexを利用できるクエリに書き換えることを検討すべきだ』とあるのを見かけました。おそらくMySQL5.1の時代かそれ以前に書かれたものだとは思います(今回試した結果でも、確かに5.1ではjoin bufferは使われていなかったように記憶しています)。MySQLを使用しないことに決めたので詳しくは調べていないのですが、この差異はMySQL5.5(正確には5.4?)で導入されたBatched Key Accessから来ているのではないかと思います。ですので、必ずしも昔書かれたチューニングが現在でも適用可能であるとは限らないので、自身でもちゃんと調べた方が良いのかな、と思いました。

 

[追記]

これを書いた後に確認のため、と再実行したときに驚愕の事実が発覚しました。mysqldumpしたファイルを読み込ませた直後にクエリを実行すると確かに上のような結果になるのですが、リストア後に一度MySQLをrestartしてから同じクエリを実行すると全然スピードが違いました。リストア直後は前述の通り1分弱かかるのですが、再起動後だとなんと4秒!(ただしこれでも他のRDBMSよりは遅いです)

実際のデータで検証してたときは何度もMySQLの再起動は行ったはずで、そのときはやっぱり遅かったのでクエリがスローダウンするJOIN数の閾値が変わってるだけなんでしょうかね…(前述の通り、restartしない場合でもJOINを一つ減らすだけで高速化しましたし)。でも検証の後半で行っていたDbUnitでデータ突っ込んでクエリして、という自動化した検証の結果は間違っている可能性もあるのか…うーん。

2011/05/08

Hibernateを使用した ― O/Rマッピングは漢(オトコ)の浪漫

 

サブタイトルには今のところ意味は無いです。

Hibbernate イン アクション(及びリバイズドエディションの Java Persistence with Hibernate)は、私が人からお勧めの本を聞かれた際の回答としてよく登場するもののうちの1冊です(ちなみに~インアクションの方はすでに廃版らしく、また、Java Persistence~の方は訳書が出ていません…)。今回のQconでGavin Kingさんが来日されると聞いて、これはサインを貰わねば、と2冊スタンバイしていたのですが取りやめになってしまって非常に残念でした。

そんな行動とは裏腹に、実のところ私はHibernateをガッツリ使ったことがなく、それどころかSQL文も SELECT * FROM ○ WHERE △ = ×; 以外の書式は入門書を見ながらでないと入力できないくらいDBに縁がありませんでした。

しかし今回、遂に、Hibernateに触れる機会がありましたので、実際に使用してみて詰まったところを記載しておこうと思います。

 

ちゃんと等価性を考慮したのにうまく動かない

先日のエントリでも触れた等価性の話です。Hibernateでは(その他JPA実装プロダクトでも)、

  • @Idアノテーションをつけたフィールドは人工キーにし、RDB上でprimary key制約をつける
  • 人工キーとは別に自然キーを持たせ、RDB上ではunique制約をつける
  • 等価性、つまりequalsメソッドは自然キーを用いて実装する

というのが典型的なエンティティの実装かと思います。しかしこのように適切にequalsを実装し、これに沿う形でhashCodeを実装したとしても、@OneToManyでEager Fetchを行うと期待通り動作してくれません。これが以下のバグです。

実際の動作はコードが添付されているのでそちらを見てもらうことにして、ポイントは以下になります。

   1: @Entity(name = "Container")
   2: private static class Container
   3: {
   4: // 中略
   5:     @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST, mappedBy = "container")
   6:     Set<Item> items = new HashSet<Item>();
   7: }

ここで”fetch = FetchType.EAGER”を取り除いてやれば(@OneToManyのデフォルトはLazyなので)期待通りの動作になります。

プライオリティはMajarとして登録されていますが、Javaの常識/ORMの常識が通用しないという意味で精神的にはクリティカルなバグでした…(わざわざWikipediaの短い説明文の中にも注釈うってるくらい重要なのに!)

「まあバグだったら仕方が無い、Lazy Fetchにするか」と思っても、次のバグが追い打ちをかけます。

これは、あるエンティティのプロパティ(フィールド)をクエリ条件の対象にしようとしてエイリアスを設定すると、エイリアスが設定されたエンティティは強制的にJOINされてくっついて取れてきちゃう、つまり強制的にEarger Fetchされてしまうので、合わせ技で回避方法が無くなります…

ちなみにHHH-3538(の重複元として登録されているHHH-2049)は、このエントリを書いている当日リリースされたバージョン3.6.4で修正されたようです。このバージョンを使えるわけではないので、これはこれで悔しいです…

[追記]

…と書いた矢先、HHH-3538とHHH-2049のDuplicateが取り消されて、このバグはReopenedになりました。実際は重複ではなくこちらはまだ未解消だったということのようですね。

 

DELETE –> INSERT したいのに INSERT –> DELETE になってしまう

理由は様々のようですが、UPDATEではなくDELETEしてからINSERTしたい、というような要望を時々耳にします(心の中ではDELETE-INSERTアンチパターンと呼んでいます。前述の通りRDBの実装にあまり関わったことが無いので本当にアンチパターンなのかどうかは分からないのですが)。で、そのDELETE-INSERTアンチパターンをHibernateで実現しようとしても、HibernateはINSERT優先なのでDELETEする前にINSERTが実行されprimary key制約違反になります。

こういう事象が、DELETE-INSERTアンチパターンを明示的に適用しなくても、通常の操作で発生してしまう、というバグです。

親子関係にあるようなエンティティが@OneToManyであり、それを@JoinTable(デフォルト戦略です)で実現しているときに子の挿げ替えを行うとunique制約違反になる、というものです。join tableはparent_idとchild_idというようなカラムを持っており、JPAの機能で自動的にテーブルを生成するとchild_idにunique制約が張られます。そしてHibernateは先に述べたようにINSERT優先、つまり新しい関連を先にRDBに反映させようとするのでchild_idが重複します。

unique制約を外したり、本当は@OneToManyなんだけど@ManyToManyに変えてしまうことで回避できると思うのですが、純潔派の方にとっては気持ちが悪いでしょう。

重複元HHH-1268のタイトルを見ると、削除だけでも発生するようですね…

[追記: 書籍Hibernate辞典を読み直していたら、「07-04-02 一対多関連の洗い替えを行いたい」という節で、上で「挿げ替え」と呼んでいる処理を「洗い替え」という名前で紹介していますね。検索してみると、そこそこ通用しそうな呼び名のように思われます。]

 

Listで@OneToManyを実現するとINSERTが2回走ってしまう

トランザクション内では特定の順番に並んでいてほしいけれど、トランザクションを抜けて再度ロードしたときには順番はどうでもいい、みたいなことを実現したいことがありまして、Listがまさにそうじゃないか、とやってみたところ見事にかかりました。(Hibernateネイティブの機能でListを使う場合には順序を保持して永続化することもできますが、JPA経由の場合はSetと同じく順序は保持されません。)

報告日も比較的最近、プライオリティもCriticalと、結構大物釣り上げたかな?と見つけた当初は思っていたのですが、実はあんまり記憶にありません…まあそんなことやろうと思うのは稀ですしね。

 

と、HibernateってORMの雄であり、結構枯れてるプロダクトかと思いきや、これが中々血気盛んです、というお話でした。他にも小さなバグには出会ったのですが、さすがに私が少し扱って見つけた範囲ではすべて報告済みのものばかりでした。

 

最後に。バグではないのですが。

LazyInitializationExceptionの怪

HibernateについてWebで検索していると、結構LazyInitializationException(LIE)で困ったというような報告がなされています。実際に使用してみるまではこれが結構疑問で、「駄目な使い方ってすぐわかりそうなもんなのになあ」なんて考えていました(マルチスレッドが絡む場合は別として)。

しかし他の問題が絡むと問題が厄介になることに気付きました。

トランザクション開始 –> インスタンスAのメソッド実行(処理1) –> インスタンスBのメソッド呼び出し(処理2) –> インスタンスAに戻ってくる(処理3) –> トランザクション終了

というような流れの中で、処理1でEntityManagerからエンティティをもらって、処理2でいくつかの計算を行った後、処理3でエンティティの関連先を参照しようとするとなぜかLIEが。しかも発生したりしなかったり。

Bを調べてみると、Session(JPAを使っていますので正確にはEntityManager)でクエリを実行した際に発生した例外を握りつぶしていることがわかりました。

Hibernateドキュメントの13.2.3.節「例外ハンドリング」にはこのように書かれています。

Session が例外 (SQLExceptionを含む) を投げた場合、直ちに、データベーストランザクションをロールバックし、Session.close() を呼び、 Session インスタンスを破棄すべきです。 Session のいくつかのメソッドは、セッションの状態を 矛盾したまま にします。 Hibernate が投げた例外を、回復できるものとして扱うことはできません。 finally ブロックの中で close() を呼んで、 Session を確実に閉じてください。

なので、インスタンスB自身は例外を握りつぶしても問題なかったからそのような実装になっていたのでしょうが、インスタンスAは自分のあずかり知らぬところで関連先が参照できないような状況になっていた…ということが起こりえます。

また、LIEが発生したりしなかったりする原因は、処理2の中でループで関連先を参照し、ある条件にヒットしたらbreakする、というような処理を行っていました。参照する先はHashSetで持っているので、処理2によってfetchされる関連先は実行の都度変わります。このため、たまたま処理3が参照する関連先エンティティを処理2によって事前にfetchしているような場合であればLIEが発生しない、という事象でした。

実行する度にエラーの有無が変わる、というのはC言語時代のバッファオーバランを彷彿とさせますね。しかもこのLIEの問題を調査しようとしてデバッグプリントを追加したりデバッガでステップ実行しながら関連先エンティティを調べたりしているとその処理によってfetchが行われ現象が再現しなくなる、問題の原因となるコード記述箇所と問題が発生する箇所が異なる、といようなことも然りです。

2011/05/07

NetBeans7上でJDK7を用いてEclipseと結果が異なっていたコンパイラの挙動を確認する

JDK7はこちらからダウンロードできる最新版(Build 141)を使用しました。なお、NetBeans7のリリースニュースで出ていた”JDK7デベロッパプレビュー版”というのはおそらくこちらのBuild 140ではないかと思いますので、若干バージョンが違います。

 

NetBeans実行に用いるJDKを変更するには、以前のエントリ

で記載しましたが、現在では日本語公式サイトにも方法が載っているようです。

NetBeansを起動しメニューから”ヘルプ”>”製品について”を選ぶと以下のような表示になりました。

製品バージョン: NetBeans IDE 7.0 (Build 201104080000)

Java: 1.7.0-ea; Java HotSpot(TM) 64-Bit Server VM 21.0-b11

システム: amd64 上で動作する Windows 7 バージョン 6.1; MS932; ja_JP (nb)

[追記: NetBeans7.0をJDK7eaで動作させると問題がいくつか出ました。ですのでこの設定で常用はしない方が良いでしょう。Java7を使用したいだけであれば上記の設定は不要です。]

また、プロジェクトをJava7でコンパイルするには、プロジェクトビューア(ウインドウ左上部にツリー表示されているやつですね)でプロジェクトを右クリックし”プロパティ”を選択するとプロジェクトプロパティーダイアログが開きますので”ソース”の”ソース/バイナリ形式”で”JDK7”を選択します。

[追記: 片貝さんのエントリにおいて図入りでより詳しく説明されています。]

 

さて何から試すか…取り敢えず私が昔エントリに書いたものを。

これはNetBeans7でも解消されていないですね…当初は6.9.1で修正予定、次に7で、とずれ込んでいったのですが、残念ながら今回も未修正のようです。続いてこちら。

よしよし、JDK7で正常に実行できました。…あれ?JDK6でもうまくいくぞ…?どうやらこれは6u18で修正されたようです(6548436:Incorrect inconvertible types error)

 

気を取り直して、以降、普通の業務系Javaプログラマなら必ず一度は罠にはまるEclipseコンパイラ(ECJというそうです)とOracle JDK6で差異があった部分がどうなったかを試してみます。問題の詳細はこちらでまとめている方がいらっしゃいましたので、そのエントリを参照してください。

 

『BOM付きUTF-8のjavaソースファイル』

BOM付きのテキストファイルはおそらくNetBeans単体では作れないと思うので、サクラエディタで保存したものを読み込ませてみたところやっぱり駄目でした。”エラー: \65279は不正な文字です”というエラーがでます。なお、NetBeansのエディタではBOMが見えてますので手で削除できました。

 

『アノテーションでの各要素の値で配列の最後にカンマ』

JDK7でコンパイル通りました。

 

『代入演算子での Integer 変数への Double 変数の加算』

Bug Databaseでは見つけられませんでしたが、下のbugの関連先でしょうかね?

ちなみに、double型(及びDouble型)からInteger型への変換が暗黙的には行えない、というのが問題の本質で、DoubleからのアンボクシングとIntegerへのボクシングを行ってもらえるよう、それぞれ明示的に型キャストしてあげればJDK6コンパイラに受け入れられるみたいです。

   1: Integer i = 1;
   2: Double d = 2.0D;
   3: i+=(int)(double)d;

確かにコンパイラがDoubleとint, doubleとIntegerの関係なんか知るか!って言いたい気持ちも分からなくはないですが、あんまりこんなコードは見たくないですね…

[追記: 元々の式は (int)+(double) だったのに対して上の式は (int)+(int) になっているので上の式は誤りですね。まあ、あくまでコンパイルを通すための手段の例示、ということで。]

JDK7では元のコードのままでコンパイルが通りました。これをJD-GUIでデコンパイルすると

Integer.valueOf((int)(localInteger.intValue() + localDouble.doubleValue()));

のような形になりました。Eclipseと一緒ですね。

 

『ジェネリックパラメータ』

最新のJDK6では修正されていました。6u22-revというバージョン表記が何を意味しているのか理解していないのですが、私のインストールしているJDKで確認したところ、6u20,6u21,6u22はNG、6u25はコンパイルOKでした。JDK7でももちろんOKです。

一番最初に記載したEnumの問題に次いでこれでよく引っかかってたのですが、そうか、JDK6を最新版にすれば大半は解消するんですね…

 

『拡張for文 (foreach文) のキャスト』

bugdatabaseと格闘する集中力が切れました…

JDK7では正常に実行終了します。JDK7でコンパイルしたものを先程と同じくJD-GUIで見てみると、

   1: for (Number localNumber : localArrayList2) {
   2:   System.out.print(localNumber);
   3:   System.out.println();
   4: }

あれ、拡張for文が展開されていないですね。仕方が無いのでjavap –v の出力結果をJDK6のものとJDK7のもので比べてみると、JDK6では

50:  checkcast       #11; //class java/lang/Double

となっているコメントが、JDK7では

50: checkcast     #11                 // class java/lang/Number

になっていました。

 

結論としては、Java7では目につくEclipseコンパイラとの動作差異は粗方無くなっていると見てよいのではないでしょうか。

2011/05/05

JavaScriptで文字列の等価性を==で評価できるのは文字列が基本型だからなのか

 

…というような疑問点が以下のエントリを読んで浮かびました。

 

表題のことを考えるついでに、私も普通の業務系(Java)プログラマが知っておくべきことを絡めてこのエントリを書いていきたいと思います。

 

まず、JavaプログラマがWebブラウザ上でリッチクライアントを実現する場合、JavaScriptを生で触る以外にも選択肢があります。GWT(Google Web Toolkit)は、JavaプログラムをJavaScriptへ変換してくれるツールキットです。これを用いることで

  • JUnitなど、今までJava開発で用いてきたライブラリや、それらのライブラリを使用してきた経験を生かすことができます
  • (プログラマが記述するのはJavaコードなので)JavaScriptにはない、タイプセーフな環境の上で開発が行えます

GWTの登場でJavaプログラマがJavaScriptを利用したアプリケーションを作り上げることはより容易になりましたが、実際にJavaScriptを導入する、すなわちWebブラウザをクライアントとしてシステムを構築する場合にはプログラミングスキルだけではなく、その他色々な検討が必要になると思います。

  • (普段Webを利用しているとあまり気づかなかったり問題の無いレベルのように感じるかもしれませんが)業務アプリは普段目にしているようなWebアプリより局所的には高い性能が求められることがあります。例えば一画面中に表示する必要がある情報量など。何となくWebアプリはJavaアプリより軽量だと感じているかもしれませんが、それはあなたの利用しているWebサービスがそういうふうに工夫しているからであって、単純にあなたが作成している業務アプリをWebアプリへ移植すると、ブラウザのメモリ使用量が大変なことになるでしょう。そしてあなたのアプリに求められている要件に対しては工夫だけでは如何ともしがたい可能性があります。
  • Ext GWT(GXT; Ext JSのGWT移植版です)を利用することなどにより、リッチなGUIを実現できるようにはなりました。が、やはりネイティブアプリに比べると実現できることには限りがあります。そしてGUIを凝ったものにすればするほど重くなります。
  • JavaはJVM上で動作しますが、同様に、JavaScriptはWebブラウザ上で動作します。そしてWebブラウザの壁はJVMの壁より厚いです。OSネイティブな機能を用いれば実現できることがわかっていたとしても遠回りをしなければならなかったり、そもそも実現が不可能だったりします

もしかすると、再度Appletに目を向けてみる良い機会かもしれません。

 

本題に入ります。

表題にさらっと「==で等価性を評価」と記載していますが、ここですでに普通の業務系Javaプログラマが知っておくべき点が隠されています。

  • == は言語によって定義が異なります。特に、軽量プログラミング言語に分類される言語は、一般的にJavaの定義とは異なります。
    • Javaでは(==をオブジェクトに対して用いるとき)”同一性を評価するのに対し、他の言語では”等価性(論理的等価性、等値性とも)”を評価するのに用いられる場合があります。

同一性、等価性については普通の業務系Javaプログラマなら持っているであろう書籍Effective Java第2版の項目8「equalsをオーバーライドするときは一般契約に従う」、あるいはJavaDocのequalsメソッドの説明を読んでみると理解できると思いますが、要するに同一性の評価とは前述の通りJavaオブジェクトに対する==、等価性の評価とは(適切に実装された)equalsメソッドです。

冒頭で示したエントリでは、

Javaプログラマー的な発想では、strとstr2は別々の文字列オブジェクトを参照しているため、最後の行の==による比較がfalseとなるように勘違いしそうですが、JavaScriptでは基本型として値の比較となるためtrueとなります。

とあり、Javaは文字列がオブジェクトだから==では正しく評価できず、JavaScriptは基本型だから正しく評価できる、というように読めますが、可能性としてはこれ以外にも『==の定義がJavaとJavaScriptで異なる』ということが考えられます。事実、Javaと同じくJVM上で動作するGroovy言語において、文字列はオブジェクトであるにも関わらず、以下の評価はJavaScriptと同様trueとなります。

   1: str = "Hello";
   2: str2 = "Hello World".substring(0, 5);
   3:  
   4: println (str == str2); // true

これはGroovyが==を(Javaとは異なり)等価性の評価として使用しているからです。以下にGroovyサイトの”Japanese Differences from Java”良くある落とし穴、の節を引用します。

すべての型において==はequalsを意味します。Javaには、==が基本型については同値性を意味し、オブジェクトにとっては同一性を意味する、という構文上奇妙な部分があります。Groovyではautoboxingを導入しているため、このことはJava開発者の大混乱を招く恐れがあります (なにせxが5のとき、x == 5はほとんどfalseになるでしょうから:)。そこで、単純化のためにGroovyでは==はequals()を意味することになっています。もし本当に同一性が必要なときは、foo.is(bar) というように "is" メソッドを使うことができます。これはnullには使えませんが、その場合には==を使って foo==null とすることができます。

この引用の中で、”同値性”という言葉が出てきます。同値というのは、書籍JavaScript 第5版から引用すると「通常、バイト単位で比較して等しい」(p.42)という意味です。

等価性、同値性、同一性の関係をベン図示しておきます(クリックで拡大)。

equality

つまり、同一であれば同値であるし、同値であれば等価でもあります。ただし逆は成り立ちません。余談になりますが、文脈によっては「等価である」というのは、言外に「同値ではないけれども」と含んでいる場合もありますので注意が必要です(同値と同一の関係の場合も同様)。

 

それでは、JavaScriptではどのような定義になっているのでしょうか。ECMAScript(3rd Ed.)の仕様を日本語訳されている方がいらっしゃったのでそちらのサイトを引用させて頂きます。

文字列に対して==を使うと

11. Type(x) が String ならば、x と y の文字シーケンスが完全に同じ(同じ長さで対応する位置に同じ文字がある)なら true を返し、そうでなければ false を返す。

という定義で評価される、という仕様になっています。つまり、Stringは同値性によって評価されます。この定義を見ればわかる通り、文字列が基本型だから、ではなく、文字列に対して==が同値性評価として機能するから、というのが正しいでしょう。この説明に「等価性」という言葉をねじ込むと、『JavaScriptの==は(同一性ではなく)等価性を評価する演算子である。また、文字列に対して等価性は同値性を以て判断される』ということになるでしょうか。

結果だけを見ると、==を用いるとき、Javaの基本型では同値性評価、JavaScriptの文字列に対しても同値性評価、と同じように見えます。しかし、だからといって『JavaScriptの==はJavaと同じく基本型に対しては同値性の評価を行う』と覚えてしまうと、 true == 1 がtrueになるのかfalseになるのか、あるいはそれ以外なのか判断を誤ります。『同じ値ではないからfalseなのかな?JavaではコンパイルエラーになるけどJavaScriptはコンパイラ言語ではないから実行時エラー?』先に示したECMAScriptの仕様を読めば正解は一目瞭然です。

従って、

  • 同じ記号だからといって異なる言語間で意味が同じだと思い込んで使用するのは、特に==演算子においては危険
    • (そもそもVBのように==演算子が無い言語や、C++のように演算子をオーバロードできる言語もあります)

ということを念頭に置いておくべきでしょう。もちろん言語設計者は既存の言語を参考にしてはいるでしょうが、それは言語間に差異が無いということを意味しているわけではありません。==ではその差異が顕著に表れています。

あと、長々と記述してきましたが、書籍JavaScript: Good Partsでは、JavaScriptの悪いパーツの筆頭に==が挙げられており邪悪な演算子呼ばわりされてますので、定義を暗記するより先に

  • ==演算子は極力使わない

ということを覚えておくのが賢明かもしれません。

 

最後に。冒頭で示したエントリの、JavaScriptには整数と浮動小数の区別が無い、という例示コード、

   1: var intValue = 1;
   2: var doubleValue = 1.0;
   3: // (中略)
   4: // 整数と浮動小数は区別がない
   5: console.log(intValue == doubleValue); // true

これをJavaで同じように書くと

   1: int i = 1;
   2: double d = 1d;
   3: System.out.println(i == d);

となります。Javaには整数と浮動小数の区別があるわけですが、実行結果がどうなるか、そしてその理由は何なのかは普通の業務系Javaプログラマならわかりますよね?

 

[補足]

本文中の「基本型」という用語は参照元エントリに合わせて”primitive datatype”の意味で使用しています。これは、書籍JavaScript 第5版では「基本データ型」と訳されています。一方、本書では「基本型(primitive type)」という用語も登場しますが、基本データ型とは異なる概念です(「基本データ型」と対になるのは「オブジェクト(複合データ型)」、「基本型」と対になるのは「参照型」)。

…と思っていたのですが、参照元エントリをちゃんと読み返すと「基本型」を両方の意味で用いていますね…

[読書]プログラマが知るべき97のこと その1 『19:誰にとっての「利便性」か』

 

本書プログラマが知るべき97のことを読み進めているといくつか過去の経験から言いたいことが出てきたので、これはメモ取りながら読んだ方が良いな、と思っていたのですが、そんな環境で本を読む時間がほとんどないことに気付きました(普段読書するのは吊革につかまりながら、なのです)。

また、途中まで書いたメモを読み返すと、結構背景補足を行わないといけないことに気付き、これは私がこの本のようにエッセイを書くのと変わらないな、と挫折しました。

そんなわけで「その1」と銘打ってはいますが(いつもの通り)続編があるかどうかは分かりません…

ちなみに、本書に収録されているエッセイは、CC-by-3.0-USによってライセンスされているそうで、オライリーのサイトで原文を閲覧できます(日本語版では日本の方が書かれた10本のエッセイが追加収録されていますので、本書の全てのエッセイが、というはけではないです)。日本語訳については一部のようですがWikisourceで読めます

 

本章『誰にとっての「利便性」か』では、作る側の利便性よりも使う側の利便性を考えてAPIを設計しましょう、という内容が書かれています。詳細は他の方がすでに書かれていますのでそちらを参照するのが良いでしょう。

そして、以下のような具体例が挙がっています。

たとえば、 walk(true) というようなコードを書かされるよりは、単に run と書ける方が間違いなく使いやすいでしょう。walkとrunは本質的には同じ動作で、ただスピードが違うだけとみなすこともできるのですが、言葉が2つある方が使う側にとっては便利なのです。

私は、著者の主張『作る側の利便性よりも使う側の利便性を考えてAPIを設計しましょう』というのには賛成ですが、walkとrunの例には賛同できません。ただスピードが違うだけなのであれば、ただスピードが違うだけなのだと使用者に伝えるべきで、それを最も明示できる方法の一つがメソッドシグニチャによる表明だと考えます。

今回の例ではwalkとrunの2種しか登場ませんが、これより種類が多い、あるいは種類が多くなる可能性があることを考えてみましょう。おそらく日本の列車種別のように特急、準急、快速ってあるけど結局どれが速いの?ということになるでしょうし、動く(walk/run)以外のAPIも同じように並んでいるとメソッド名を見ただけでは判断がつかなくなると思います。これは使う側の利便性も低下していることになるでしょう。

この例で、私が採ると思われる策は、速さをenumの引数で表すことです。つまり move(Speed speed) のような形になります(Speedがenum)。

  • runとwalkという文字列で表したいのであれば、そのようにSpeedを定義すれば良い。
  • 速さの種類が増えてもインタフェースの互換性は失われない。また、前述の列車種別のような例でもenumの定義順から速さの順番は推測できる。
  • Javaではenumでストラテジパターンも実現可能であり、設計に幅ができる。

(JavaのEnum詳細については書籍Effective Java 第2版 「項目30 int定数の代わりにenumを使用する」を参照してみてください。)

…もしかすると、著者と私で想定している背景が異なっているため、上のような結論の差異が出ているのかもしれません。実際に設計する際には「あの本に載っていたから」を理由にするのではなく、前提条件がその本と一致しているか、自分の問題にも適用するのが最適なのか、などといったことも考慮する必要がある、というのを覚えておくべきでしょう。

 

最後に。本書は「(比較的著名な)プログラマ97人がが考えている、知っておくべきこと集」であって、「プログラマが知るべきことのうちBEST97」ではないことに留意しましょう。もし今年SEになったばかりの人に何か良い本は無いかと尋ねられた場合、おそらく私が本書を勧めることは無いと思います。本書が悪書である、と言いたいわけではなく、新人SEが本書より先に読むべきものは他に沢山ある、ということです(個人的には、本書はファンブックに分類されるものだと考えます)。

(補足: 便宜上プログラマ97人と書きましたが、実際には1人の方が複数のエッセイを書かれていますので正確には73人です(前述したとおり日本語版では+8名)。)

2011/05/04

PlayStation Network 個人情報漏洩 #psnleaks について電話サポートで質問してみた

XBox Liveの件に続き、こちらでもトラップにかかってしまいましたのでサポートで質問してみました。あんまり大した情報は得られませんでしたが一応記載しておきます。ちなみに電話が担当者につながるまで待ち時間は無かったです(自動応答のお詫び朗読時間はちょっと鬱陶しかったですが)。

 

1.漏洩が発覚するより以前からクレジットカード認証に障害が発生していた利用者が多くいたようだが、今回の件との関連はあるのか

今回の事象との関連は確認できていない。なお、クレジットカードの利用が行えなくなる原因としては

  • 入力情報に誤りがある
  • 認証サービスに障害が発生した
  • 頻繁に情報を入力しなおすと認証にロックがかかる仕組みがあり、それが適用された(後述2ch.netスレ内にある、サポートで72時間待てと言われた、というのはこの件だと推測されます)

の3点が考えられ、これらのいずれかに該当していた可能性もある。

2ch.netのPlayStation Store不具合スレにあるように、遅くとも今年の頭以降、PSNでクレジットカード利用ができない人が複数人いました。

私は今年の3月末から利用し始めたのですが、初回購入は正しく認証できたものの、2回目以降情報を更新していないにもかかわらず認証ではじかれる、という事象が発生していたのでついでに聞いてみました。当時もサポートに連絡しようとしたのですが、震災の影響でメール/電話サポート共に止まっており、結局他のカード番号を入力して試してみる、という、今考えるとセキュリティ意識に欠ける行動をとってしまいました…

 

2.いつの時点の個人情報が漏えいしたのか

現時点の調査では、可能性が高いのはサービス停止時に登録していた個人情報、ということだそうです。

ただクラッキングを自動で検知してサービス停止していた、のような事実でもない限り上記の回答を鵜呑みにしても仕方が無いですね。調査が進むとこの点も明らかになってくるかもしれませんが、今の時点では入力したことのある全ての情報に対して、と考えておく方がより防御的でしょう。

前述の通り、私の場合は複数種のカード番号を入力したりしているので手続きが面倒なことになりそうです…

 

3.セキュリティコードの漏洩は無い、というのは確認が取れている事実なのか

回答としては、今のところ利用履歴などからセキュリティコードが漏洩した事実は確認されていない、とのことでした。

セキュリティコード入力について、最近PSNを利用していなかったので入力していないと思い込んでいる、という方も多そうなので補足をすると、それは思い違いです。公式サイトのQ&Aにも入力方法について記載があります

音元出版社のニュース記事『ソニー、PSN/Qriocity情報流出の新情報を公開 - カード情報は暗号化、個人情報は暗号化せず』という記事の中で

Q:クレジットカードのデータは盗まれたのか?
A:(中略)これはぜひ覚えて置いて欲しいが、CVCやCSCというセキュリティコードは、第三者に取得されていない。これは、我々がPSN/Qriocityについてこの情報を送信するよう顧客に要請したことがなく、システムにもともと情報が存在しないからだ。

というソニーのSony Computer Entertainment America(SCEA)社公式blog “PlayStation.Blog” のエントリQ&A #1 for PlayStation Network and Qriocity Services和訳が掲載されています。この記事の転載がweb上で出回っているのを何度か見かけましたが、この点についてSCEA社のエントリは既に更新されています。

Keep in mind, however that your credit card security code (sometimes called a CVC or CSC number) has not been obtained because we never requested it from anyone who has joined the PlayStation Network or Qriocity, and is therefore not stored anywhere in our system. UPDATE: While we do ask for CSC codes, we do not store them in our database.

この記述から推測するに、現時点でSCE社は

  • 漏洩した個人情報は多くともデータベースに保存されているものに限られる

という見通しを立てているように思われます。が、それが事実かどうかは今の段階の調査で断言してよいのだろうかとは思います。今の時点での回答はあんまり期待しない方が良さそうだ、というのが感触でした。

 

4.個人情報漏洩に対する被害が発生する前に、予防的にクレジットカード番号の変更を行った際の手数料をSCE社が補償するつもりはない、という認識は正しいか

対応については検討中のものあり、決定したものは公式サイトに掲載する、とのことでした。まあ「ありません」とか回答すると電話口でブチ切れられるだけでしょうし、あんまり期待しないで質問してみました。…というか、今FAQ見たら5月3日更新分に同じ内容のことが書かれていますね。確認漏れでした…

 

モバゲーなんかも個人情報の削除には対応してくれない、という話もあるそうですが、法律で規定されていないから削除しない、という運営方針で今後もコンセンサスが取れていると主張し続けられるのかは気になるところです。

 

サーバーとの通信を2重暗号化するなどの対策が急務:三輪信雄「ここが変だよみんなの対策」では

今回の攻撃方法がどのようなものであったかは分かりませんが、一つの可能性としてゲーム機の通信を解析することによって、サーバーの脆弱性を暴いて、それを悪用した可能性があります。
(中略)サーバーとの通信を密に行うようなケースでは、サーバーとの通信の2重の暗号化などの総合的な対策が必要とされます。

というようなことが書かれていますが、ゲーム機で実現しているオンライン対戦ではレイテンシ短縮も重要なので、暗号化/復号処理が複雑になるのは、それはそれで問題でしょうね。というか、本当に記載している内容が仮に事実だったとすると、そんなことをするよりクレジットカード等の情報が必要なAPサーバだかDBサーバだかとそれ以外に使用するものを分ける方が優先順位は高いと思いますが…

2011/05/03

QCon Tokyo 2011 #qcontokyo に参加した

 

4月12日(火)に開催されたQCon Tokyo 2011に参加してきましたのでその感想などを。文中の敬称は畏れ多いですが「さん」で統一してます。

 

10:00 – 12:00 基調講演

基調講演はEric Evansさんと伊藤直也さんが行われたのですが、当日の朝、比較的大きい地震があり電車が止まってしまいましたのでほとんど参加できませんでした…特に私の一番の目当てであったEric Evansさんのお話が全く聞けなかったのは痛かったです。

伊藤直也さんのお話もほとんど聞けず。基調講演で記憶に残っているのは『グリーのサービスでは既にAndroid利用者数がiPhone利用者を超えている』という1点だけです。なんとなくそういったサービスを利用している人はAndroid?iOS?なにそれ?みたいな人が大半で、そういう人はAndroid端末よりiPhoneを選ぶだろうと考えていたので意外でした。

 

13:00 – 13:50 クラウドのデータアーキテクチャ―設計の原則 萩原 正義 さん(日本マイクロソフト株式会社)

クラウド(型)アーキテクチャを採用する上で最適化にはどういった考慮が必要になるか、というお話で、具体的にはMapReduceでの例、Columnar(カラム指向データベース())での例を説明されていました。Columnarの考え方は普通にRDBでも適用できるんじゃないかな、と思っていたところ、『RDBMSのオプティマイザをあまり舐めない方が良い 』みたいなこともお話しされていたので、そんな私の考えも「何を今さら」という感じなのでしょう。

他には、半導体レベル(FPGA、と説明されてたかな?この辺明るくないので聞き間違っているかも)での最適化の試みもある、というようなことに少し触れられていました。いわゆる業務SEでもそっち系の知識が求められる時代が来るのでしょうか、空恐ろしい…しかし、ある業務に最適化されたハードウェアと”クラウド”って相反するところがあるような。そこまでの最適化ではなく、MapReduceとかHadoopとか、そういったミドルウェア的プロダクトレベルの最適化になるんでしょうかね。

他の方のtweetでは本セッションは基本的な事項に留まっていた、というような意見もありましたが、ぶっちゃけ私ははてなのRDB vs KVS煽り合いホットエントリみたいなところで得た知識しかなかったので、MapReduceって何をするプロダクトなのか、というところから学べたので有意義でした。

 

14:05 – 14:55 品質検査技術のトレンド -レビューと測定・欠陥工学を中心に- 細川 宣啓 さん(日本アイ・ビー・エム株式会社)

申し訳ないことに当日まで細川さんのお名前を存じていなかったのですが、参加してみてびっくり、個人的に超大穴セッションでした。

以前会社に所属していたとき、その会社から品質管理専任としてプロジェクトに来られる方は、はソフトウェア開発スキルが低く、かといってプロジェクトマネジメントもできない方ばかりでした(ああ、これが大企業病の一症状なんだなあ、と思ったものです)。

そういった方たちの持っている品質管理スキルは結局開発者が持っているスキルのサブセットでしかなく、提案された方法も、既により効率的なやり方でやっていたり、手間を複雑にしてるだけだったり、開発者から見て安心・信頼できるとは言い難かったです。

細川さんは、レビュアが的確な指摘を行うために必要なものは経験だ、とおっしゃっていました。ではその経験はいつ得られるのかというと、現状では実際に自分で開発したとき、だけなのではないかと思います。自分が失敗し、それをどうリカバリしたり、あるいは本来どうすべきだったのか考えることでしか得られるようになっていない、ということです。そして、品質管理という立場では、自身が行ってきたことと発生した問題の因果関係が明確になりにくいため、開発者ほど真剣に失敗に取り組んでいない(つまり、開発者ほど大した経験が得られていない)ように感じます。

資料p.22以降にもある通り、細川さんは、品質管理の専門家となるための体系だった知識が未発達であり、この問題の対応策として、バグを世界規模で収集し類型化できるようなデータベースの構築に取り組んでいるとおっしゃっていました。私も、このような分野がシステム開発の一分野として発達すれば、前述したような品質管理者のスキル不足が解消され、専門家としてプロジェクトの中で活躍できるのではないかと考えます。

セッション内容の本筋からは逸れますが、ソフトウェアはバージョンが上がる度(つまり機能が増える度)に品質は下がる、とおっしゃっていました。また、後述Twitter社のEvan Weaverさんは、Twitter社で働いている時間のうち半分はコードを削減するのに費やしてきた、と語っていらっしゃいました。ソフトウェアを成長させていくには、過去のコードに対しても大きな労力が実際には必要であるということです。にもかかわらず、典型的な請負型契約ではそういったことは行えない、といった問題についても、品質管理分野が発達すれば解消されるのかな、と考えたりもしました。

 

15:10 – 16:00 サービスのスケーラビリティと無停止のためのインメモリ技術 杉 達也 さん(日本オラクル株式会社)

Oracle社が買収する前の、Tangosol社時代のCoherenceを少し触った経験があるので、回顧目的で参加しました。私の目的は達成されたわけなんですけれども、技術セッションというよりOracle社のCoherenceプリセールス活動的な色が非常に濃かったので、他の聴衆の皆さんが満足されたのかは非常に疑問が残りました。

前述した萩原さんクラウドアーキテクチャに関するお話を聞いているときに感じていたのですが、Coherenceも自分の欲しいデータがローカルに無い(正確には、有るとは限らない)点やKeyの性質でValueを分散させる点などが似通っているのではないでしょうか。従って、KVSの考え方が応用できたり、また、最適化の際にはKVS的な考慮が必要だったりすると思っています。せっかく世間でクラウドが流行っているのだからそういう点からアプローチすれば良いのに、と感じました(繰り返しになりますが、KVSちゃんと知ったの当日が初めてなので誤解しているかもしれませんが)。

そういえばJavaSpacesという仕様があって、その実装プロダクトであるGigaspacesと比較されることがしばしばありましたし、詳しくは知らないのですがGemstoneやTerracottaといった同等のプロダクトもあるようです。本セッションではJavaEEとの親和性の高さを強調していらっしゃったので、そういった機能でCoherenceと他社プロダクトを差別化する、ということなんでしょうかね。

 

16:15 – 17:05 Performance Engineering at Twitter Evan Weaver さん(Twitter社)

PDFになってしまったセッション資料を見ると色々とはみ出してて不恰好に見えますね…実際の資料は、PDFの最後のページ(28ページ目)が最初に映され、説明が進む度にシームレスに各項目がズームインされる、といった形で、超カッケー感じでした。Preziで作成されたものらしく、こちらのサイトで見られる作品で、どんなものだったかイメージがつかめるかと思います。

一番時間が割かれたのはは、こちらにあるTwitter社の記事についてのことでした。Matz' Ruby ImplementationやRuby Enterprize Edition(RubyにEnterprize Editionなんてものがあるのも本セッションに参加して初めて知りましたが、サイトを見るとGC改良に力を入れているようです)では満足のいくGCパフォーマンスが得られず、自社で改良することにした(Project Kiji)ところ、良い結果が得られた、ということでした。JVMで採用されているような世代別GCを実装したそうです。

セッションの初めの方ではjemalloctcmallocについても触れられていました。が、ちょうど話されていることを直接英語で聞き取れないか奮闘していたため(無駄な努力でしたが)内容分かりません…マルチスレッド環境下ではjemallocの方がパフォーマンスが良い、ということだった、かも。

TwitterサービスはRubyからJVM言語(JavaやScala)に移行している部分もありますが、JVMも大きなオブジェクトのGC管理においては満足のいくパフォーマンス管理が得られていないようです。また、今からTwitterを構築するとしたらどんな言語を使いますか、という質問に対してはJRubyも回答に入っていたことから、Rubyの言語仕様(や言語仕様の決まり方)自体に問題があると考えているわけではないのかな、と思いました。

 

17:20 – 18:10 ドメイン駆動設計においてレガシーシステムを扱うための戦略 Eric Evans さん

残念ながら途中までしか参加できませんでした…dddcommunity.orgのEscaping Legacy while Avoiding Strategic Trapsというエントリに見覚えのある図が出ていましたので、これと似た内容だったのかもしれません。

途中まで参加した内容と、上記リンク先のビデオ(英語は聞き取れないのでスライドからの想像ですが)から想像するに、書籍エリック・エヴァンスのドメイン駆動設計の第4部「戦略的設計」を中心にお話しされたのではないかと思っています。この部分は翌日開催されたDDDチュートリアルの後半でも、寸劇を採り入れるなど時間をかけて解説されていましたのでこの部分重要!ってことなんでしょうね。

 

そんなわけで始めも終わりも中途半端になってしまいましたが、その辺は他の方のblogで補完を、ということで、検索してヒットしたblogのリンクを載せてみました(実際にはもっと多くの方が感想を書かれていると思いますが、私が検索できた範囲で…)。

 

最後に。『QCon Tokyo 2011 にご来場いただいた方の中から希望者全員に「日経コンピュータ」(通常 19,800円)もしくは、「日経SYSTEMS」(通常13,800円)の年間購読を無料でプレゼントいたします。』というのがあったのですが、申し込み用紙出すのをすっかり忘れていました…

« 2011年3月 | トップページ | 2011年6月 »

other sites

  • follow us in feedly
  • github
  • stackoverflow

ソフトウェアエンジニアとして影響を受けた書籍

  • Christain Bauer: HIBERNATE イン アクション

    Christain Bauer: HIBERNATE イン アクション
    理論と実践が双方とも素晴らしい製品であるHibernate。本書はそのプロダクトを書名に冠していますが、Hibernateを使うつもりがなく、ORマッピングの解説書として読むにしても十分な良書です。Second EditionとしてJava Persistence With Hibernateという書籍も出版されていますが、残念ながら現在のところ 和訳はされていません。-インアクションは2.xの、Java Persistence-は3.1の頃のものなので、最新版とはちょっと違うところもあることに注意。 (★★★★★)

  • アンドリュー・S・タネンバウム: 分散システム 原理とパラダイム 第2版

    アンドリュー・S・タネンバウム: 分散システム 原理とパラダイム 第2版
    クライアント/サーバシステムを構築する上で必要となる知識が総論されてます。Web技術者も、フレームワーク部分を開発するのであれば必読。 (★★★★★)

  • Joel Spolsky∥著: ジョエル・オン・ソフトウェア

    Joel Spolsky∥著: ジョエル・オン・ソフトウェア
    前述の書籍「ソフトウエア開発プロフェッショナル」をより砕いたもの、という感じでしょうか。 前書きではプログラマでなくSE向けの本のように書かれているが、プログラマが読んでも面白い本であると思われます。 SEになった新人(あるいはそういう会社に入る/入りたての人)にとっては、これからどういったことが仕事を遂行していく上で起こりえるのか、どのように考えて行なっていけばいいのか決定する助けになると思います。 元は″Joel on Software″というブログの記事で、web上でも一部日本語で読めます。 http://japanese.joelonsoftware.com/ (★★★)

  • ドナルド・C・ゴース,ジェラルド・M・ワインバーグ: ライト、ついてますか

    ドナルド・C・ゴース,ジェラルド・M・ワインバーグ: ライト、ついてますか
    問題解決(一昔前のの流行語で言うところの『ソリューション』)能力は、システムエンジニアのスキルとして備えるべきもののうちのひとつです。しかし、これは難しい。学校で出されるテストと違い、唯一の、(問題提出者が想定している)解を求めるだけが「問題解決」では無いからです。そもそも、何が問題なのか、それは本当に問題なのか、それは本当に解決すべき問題なのか、その問題解決方法は正しいのか、などを解決しなければ、「その解は正しいのか」に辿りつくことができません。この本の最も良いところのひとつは、本があまり厚くないこと。すぐに読めるし、何回も読み返す気になるでしょう。 (★★★★★)

  • スティーブ・マコネル: ソフトウエア開発プロフェッショナル

    スティーブ・マコネル: ソフトウエア開発プロフェッショナル
    コードコンプリートで有名なスティーブマコネルの著書。新人SEに読んで欲しい。個人として業界の中でどうあるべきか、組織としてどうあるべきか、SEのプロ意識とは?SEの心構え概論、といったところでしょうか。また、業界における資格の重要性についても説かれています。この業界では資格が特に軽んじられる傾向がありますが、この傾向はどんな弊害をもたらすのか、将来的にこの業界は資格に対してどのような姿勢で臨んでいくべきなのか。日経BP社では(他の出版社もだが)最近、似たような類いのあまり面白くない書籍が乱出版されていますが、この本は別格だと思うので安心して購入して欲しいと思います。 (★★★★★)

無料ブログはココログ