2014年12月16日火曜日

Sinatra + SSL

Found a better way to enable SSL in Sinatra than "Sinatra+Thin+SSL". This is a generic way and does not require Thin.

2014年10月1日水曜日

Sinatra + Thin + SSL

Finally found a way to enable SSL in Sinatra + Thin.

# [2014/12/16]
# Found a better way.
# See "Sinatra + SSL".


Start this script (sinatra+thin+ssl.rb) then type:
curl -k https://localhost/
and you will see "Hello, SSL."

2014年9月20日土曜日

MissingTableException on Google Cloud SQL

If you are using JDO to access Google Cloud SQL and suffering from MissingTableException like below:

org.datanucleus.store.rdbms.exceptions.MissingTableException: Required table missing : "`TABLE NAME`" in Catalog "" Schema "". DataNucleus requires this table to perform its persistence operations. Either your MetaData is incorrect, or you need to enable "datanucleus.autoCreateTables"

and if your table names contain lowercase letters, probably the issue can be solved by setting "datanucleus.identifier.case" properly.

"JDO : Datastore Identifiers" says "By default, DataNucleus will capitalise names". Therefore, if your table names contain lowercase letters, "datanucleus.identifier.case" should be set explicitly.

The valid values for "datanucleus.identifier.case" are UpperCase, LowerCase or PreserveCase (or MixedCase; See Change "datanucleus.identifier.case" 'PreserveCase' to be 'MixedCase' to be match internal namings). So, for example, your jdoconfig.xml will have to have the entry like the following.

<property name="datanucleus.identifier.case" value="LowerCase"/>

If you are developing your GAE application on Windows or Mac OS X, you may not encounter this case sensitivity problem until you deploy your application onto GAE. See "9.2.2 Identifier Case Sensitivity" (MySQL Reference Manual) for details.

2014年7月19日土曜日

JDO:カラムサイズは十分大きいのにデータが保存できないという問題を解消する方法

  JDO を使っているときに表示される次のようなエラーメッセージは、

Attempt to store value "{DATA}" in column "`{COLUMN-NAME}`" that has maximum length of 255. Please correct your data!

保存しようとしているデータのサイズが、対応するデータベーステーブルのカラムのデータサイズよりも大きいということを意味している。しかし、カラムサイズが十分大きくてもこのエラーが起きることがある。例えば、対応するカラムの型を TEXT で定義しているにもかかわらず(MySQL 的には TEXT は 65535 まで OK であるにもかかわらず)、上記のようなエラーが出てしまうことがある。

  この問題は、@Column アノテーションに length を追加して、最大長を指定すれば解消するようだ。具体的には、

@Persistent
@Column(name = "comment")
private String comment;

となっているところを、

@Persistent
@Column(name = "comment", length = 65535)
private String comment;

というような具合に変更する。


2014年7月10日木曜日

JDO Enum Mapping

  Java Data Objects 3.0 (JSR 243) の仕様によると、Enum の値をストレージに永続化する際、ストレージでの型が数値型であれば Enum.ordinal() メソッドの返す値が、文字列型であれば Enum.name() メソッドの返す値が用いられるとのこと。 (15.1 Column Elements / Mapping enums)

  Enum にマッピングされるカラムのデフォルトの jdbc-type は VARCHAR なので、ordinal() の値を使いたいときは、当該カラムに対して明示的に jdbc-type を指定したほうがよい。有効な jdbc-type は次のとおりで、全て大文字か全て小文字かのどちらかで指定する。 (18.4 ELEMENT column)

CHAR, VARCHAR, LONGVARCHAR, NUMERIC, DECIMAL, BIT, TINYINT, SMALLINT, INTEGER, BIGINT, REAL, FLOAT, DOUBLE, BINARY, VARBINARY, LONGVARBINARY, DATE, TIME, TIMESTAMP

  DataNucleus は、name()ordinal() ではない別の値で Enum を永続化する拡張を提供している。下記は、「JDO : Persistable Field Types」 に挙げられている例である。

public enum MyColour 
{
    RED((short)1), GREEN((short)3), BLUE((short)5), YELLOW((short)8);

    private short value;

    private MyColour(short value)
    {
        this.value = value;
    }

    public short getValue() 
    {
        return value;
    }

    public static MyColour getEnumByValue(short value)
    {
        switch (value)
        {
            case 1:
                return RED;
            case 3:
                return GREEN;
            case 5:
                return BLUE;
            default:
                return YELLOW;
        }
    }
}
@Extensions({
    @Extension(vendorName="datanucleus",
               key="enum-getter-by-value", value="getEnumByValue"),
    @Extension(vendorName="datanucleus",
               key="enum-value-getter", value="getValue")
   })
MyColour colour;

  name() で永続化する場合は 「"RED", "GREEN", "BLUE", "YELLOW"」、 ordinal() で永続化する場合は 「0, 1, 2, 3」 であるが、上記の例のようにすれば、「1, 3, 5, 8」 という値で永続化される。

  ただ、ここに誤りやすいポイントがある。DataNucleus の拡張を使う場合、値のデータ型は short でなければならないようだ。例えば上記の実装で getEnumByValue(short value) となっているところを getEnumByValue(int value) に変更すると、拡張機能が動作しない。逆にデータサイズを小さくする getEnumByValue(byte value) でも、やはり動作しない。ただ、ストレージでの型は必ずしも SMALLINT でなくてもよいようだ。

  例えば、ClientType という enum を定義し、PUBLICCONFIDENTIAL というエントリーを持たせ、それぞれ 1, 2 という値で永続化したい場合、その実装は次のようになる。


public enum ClientType 
{
    PUBLIC((short)1),
    CONFIDENTIAL((short)2); 
 
    private static final ClientType[] mValues = values(); 
    private final short mValue;
 
    private ClientType(short value)
    {
        mValue = value;
    }
 
    public short getValue()
    {
        return mValue;
    }
 
    public static ClientType getByValue(short value)
    {
        if (value < 1 || mValues.length < value)
        {
            return null;
        }

        return mValues[value - 1];
    }
}

  この ClientType 型のフィールドを持つ ClientEntity クラスを定義するとすれば次のような感じになる。


@PersistenceCapable(
        identityType = IdentityType.APPLICATION,
        table        = "client",
        detachable   = "true")
public class ClientEntity 
{
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Column(name = "number")
    private int number;
 
    @Persistent(defaultFetchGroup = "true")
    @Column(name = "client_type", jdbcType = "TINYINT")
    @Extensions({
        @Extension(vendorName = "datanucleus",
                   key = "enum-getter-by-value", value = "getByValue"),
        @Extension(vendorName = "datanucleus",
                   key = "enum-value-getter", value = "getValue")
    })
    private ClientType clientType;

    public int getNumber()
    {
        return number;
    }

    public ClientType getClientType()
    {
        return clientType;
    }

    public void setClientType(ClientType clientType)
    {
        this.clientType = clientType;
    }
}

ClientEntity クラスに対応するテーブル "client" を MySQL で定義するならば、次のような感じ。

CREATE TABLE client
(
    number INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    client_type TINYINT UNSIGNED NOT NULL DEFAULT 1
);

2014年4月19日土曜日

2 の 256 乗を計算してみた

2256 を十進数で表すと 78 桁で、無量大数の十億倍くらいの値であることが分かりました。 ちゃんとした方法でランダムに生成した 256 ビットのハッシュ値が偶然ぶつかるかもしれないなんてことを心配するのは馬鹿げていることを理解しました。いや、馬鹿げているのは理解していたのですけど、実際に計算してみると、どれだけ途方もなく馬鹿げているのかが分かった、という話です。

どれくらい大きな数字なのか例えで考えてみます。
  1. 太陽の寿命が来るまで今から 50 億年のあいだ、
  2. 100 億人の人が、
  3. 毎ナノ秒 1 兆個の数値を消費していく活動があり、
  4. その活動を、太陽系が属するこの銀河系の全ての星(~2,000 億くらい)でおこなうと、
消費される数字の個数は、50 億年 × 100 億人 × 100000000 ナノ秒 × 60 秒 × 60 分 × 24 時間 × 365 日 × 1 兆個 × 2000 億個(星) で 59 桁の数字になります。こんなに頑張って消費しても、50 億年後 (太陽が赤色巨星になって太陽系が崩壊する頃) に消費し終わっているのは、(59 桁) / (78 桁) = 10-19、つまり全体の 100 京分の 1 です。

ちなみに、Java の long は 64 ビットで 10 進数だと 20 桁 (2000 京弱)。10 億人が毎秒消費なら1000 年くらいで使い切る計算。秒間 10 億を千年。それを超えるような想定をしなければならないシステムを作ることはないでしょうね。推測可能でよい ID なら、long で連番振っておけば十分でしょうね。


21 = 2
22 = 4
23 = 8
24 = 16
25 = 32
26 = 64
27 = 128
28 = 256
29 = 512
210 = 1,024
211 = 2,048
212 = 4,096
213 = 8,192
214 = 16,384
215 = 32,768
216 = 65,536
217 = 131,072
218 = 262,144
219 = 524,288
220 = 1,048,576 (7 桁)
= 104 万 8576
221 = 2,097,152 (7 桁)
= 209 万 7152
222 = 4,194,304 (7 桁)
= 419 万 4304
223 = 8,388,608 (7 桁)
= 838 万 8608
224 = 16,777,216 (8 桁)
= 1677 万 7216
225 = 33,554,432 (8 桁)
= 3355 万 4432
226 = 67,108,864 (8 桁)
= 6710 万 8864
227 = 134,217,728 (9 桁)
= 1 億 3421 万 7728
228 = 268,435,456 (9 桁)
= 2 億 6843 万 5456
229 = 536,870,912 (9 桁)
= 5 億 3687 万 912
230 = 1,073,741,824 (10 桁)
= 10 億 7374 万 1824
231 = 2,147,483,648 (10 桁)
= 21 億 4748 万 3648
232 = 4,294,967,296 (10 桁)
= 42 億 9496 万 7296
248 = 4,281,474,976,710,656 (16 桁)
= 4281 兆 4749 億 7671 万 656
264 = 18,446,744,073,709,551,616 (20 桁)
= 1844 京 6744 兆 737 億 955 万 1616
2128 = 340,282,366,920,938,463,463,374,607,431,768,211,456 (39 桁)
= 340 澗 2823 溝 6692 穣 938 じょ 4634 垓
6337 京 4607 兆 4317 億 6821 万 1456
2256 = 115,792,089,237,316,195,423,570,985,008,687,907,853,
269,984,665,640,564,039,457,584,007,913,129,639,936 (78 桁)
= 1157920892 無量大数
3731 不可思議 6195 那由他 4235 阿僧祇
7098 恒河沙 5008 極 6879 載 785 正
3269 澗 9846 溝 6564 穣 564 じょ 394 垓
5758 京 4007 兆 9131 億 2963 万 9936

Blogger の都合でサロゲートペアの文字が化けるため、平仮名で表記。 Blogger もうやだ・・・。

2014年3月17日月曜日

AccessControlException when using local MySQL for Google Cloud SQL

  If you are developing an application for Google App Engine + Google Cloud SQL and using local MySQL during development, you may encounter AccessControlExeption like below.
java.security.AccessControlException:
    access denied ("java.lang.RuntimePermission" "modifyThreadGroup")
  This exception is raised when your application tries to use Thread because Google App Engine does not allow application to use Thread.

  Reading the stack trace, you will find the point where Thread is created.
at java.lang.Thread.<init>(Thread.java:444)
at java.util.TimerThread.<init>(Timer.java:499)
at java.util.Timer.<init>(Timer.java:101)
at java.util.Timer.<init>(Timer.java:146)
at com.mysql.jdbc.ConnectionImpl.getCancelTimer(ConnectionImpl.java:392)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2195)
  From the information above, it can be guessed that a Thread instance will not be created if PreparedStatement.executeInternal method does not call ConnectionImpl.getCancelTimer method.

  Below is an excerpt from the source code of executeInternal method.
if (locallyScopedConnection.getEnableQueryTimeouts() &&
    this.timeoutInMillis != 0 &&
    locallyScopedConnection.versionMeetsMinimum(5, 0, 0))
{
    timeoutTask = new CancelTask(this);
    locallyScopedConnection
        .getCancelTimer()
        .schedule(timeoutTask, this.timeoutInMillis);
  The code snippet indicates that getCancelTimer method is not called if timeouts are not set.

  So, check your jdoconfig.xml. If DatastoreReadTimeoutMillis and/or DatastoreWriteTimeoutMillis are configured, comment them out during development.