SAStruts + SQLite3 の組み合わせでハマる

ふと SAStruts を触ってみようかなと思い立ち、blank をダウンロード*1 して適当に動かしてみる。「Javaでサクサク感のある開発」がウリらしいですが、確かにサクサク作れますね。

  • struts-config をまったくいじる必要がない
  • Tomcatを再起動しないでいい

のは大きいかも。

で、とりあえずDBなしの画面は簡単にできました。次にDBからデータを引っ張ってきて一覧表示するところで詰まる。
DBはSQLitejdbcの設定は以下でOK。

<component name="xaDataSource"
  class="org.seasar.extension.dbcp.impl.XADataSourceImpl">
  <property name="driverClassName">
    "org.sqlite.JDBC"
  </property>
  <property name="URL">
    "jdbc:sqlite:/"
      + @org.seasar.framework.util.ResourceUtil@getBuildDir('app.dicon').getCanonicalPath()
      + "/data/sastruts.db"
  </property>
  <property name="user">"sa"</property>
  <property name="password">""</property>
</component>

で、SAStruts側から呼び出す。

public class UserAction {

    @IntegerType
    public String id;
    public String userName;
    public String email;
    public String description;

    public List<Users> userList;
    public JdbcManager jdbcManager;

    @Execute(validator = false)
    public String index() {
        userList = jdbcManager
                    .from(Users.class)
                    .orderBy("id")
                    .getResultList();
        return "list.jsp";
    }
}

すると、ServletException が発生。

javax.servlet.ServletException: [ESSR0072]SQLで例外(SQL=[], Message=[not yet implemented], ErrorCode=0, SQLState=null)が発生しました
...
(中略)
原因:
java.sql.SQLException: not yet implemented
    org.sqlite.Conn.setReadOnly(Conn.java:140)
    org.seasar.extension.dbcp.impl.ConnectionWrapperImpl.setReadOnly(ConnectionWrapperImpl.java:214)
    org.seasar.extension.dbcp.impl.ConnectionPoolImpl.checkOut(ConnectionPoolImpl.java:322)
    ...
    (以下略)

どうやら SQLite3 の JDBC ドライバ*2が setReadOnly メソッドを実装していないらしい。念のためソースで確認したところ、確かに実装してないね。

(sqlitejdbc-v043\src\org\sqlite\Conn.java - Line 139)

    public void setReadOnly(boolean ro) throws SQLException
        { throw new SQLException("not yet implemented"); }

AOPで無理やり ConnectionWrapperImpl#setReadOnly を無効化しようかとも考えたんだが、ConnectionWrapperImpl が直接 new されており S2 の管理下ではないので S2 上で Aspect を差し込むのは難しそうだという結論に。
ConnectionPoolImpl#checkOut を sqlite 用にいじれば*3動くんだろうけど、それはそれで泥臭いなぁ...などと考えていたら1日が終わっていました。本題とは関係ないところで時間を使ってしまった。


ちなみに、ソースコードを追う過程で S2 の dicon の書き方はなんとなく分かったような気がする。Spring 経験という下地があったためか、それほど苦労なく追えた。