2009년 10월 16일 금요일

Class#getResource と ClassLoader#getResource の違いと怪しさ

Java の実装の話です。

設定ファイルやら相対パスを使ったリソース参照のために、よく getResource() や getResourceAsStream() を用います。
これらに与えるリソース名について Class#getResource() のドキュメントでは以下のような説明がされています。

委譲の前に、このアルゴリズムを使って指定されたリソース名から絶対リソース名が構築されます。

  • name が「/」(「\u002f」) で始まる場合、リソースの絶対名は「/」に続く name の部分である
  • そうでない場合、絶対名は以下の形式になる
    modified_package_name/name
    ここで、modified_package_name は、「. 」(「\u002e 」) を「/」に置き換えたこのオブジェクトのパッケージ名になる

これは実装を忠実に表しています。しかし ClassLoader#getResource() のほうではどのような記法でどこからロードを行うかは記載されていません。ClassLoader は抽象クラスであり、どのように検索を行うかは記述できないのです。ただ、先頭の / の扱いに関しては規定しておいて欲しかった…。

Class#getResource() でも最終的には ClassLoader#getResource() が呼び出されます。しかし、このメソッド自体が、クラスローダの実装によって振る舞いが異なるという問題があります。

リソース名の先頭に "/" を書くか書かないかと、Class#getResource() を呼ぶか ClassLoader#getResource() を呼ぶかで、Sun の JDK(1.4/1.5) に付属の ClassLoader 実装では以下のような挙動になります。

Class#
getResource()
ClassLoader#
getResource()
"/name"ルートから name を検索見つからない
"name"クラスのパッケージディレクトリ相対で name を検索ルートから name を検索

さて、皆さんこのあたりはご存知だったでしょうか?

ClassLoader#getResource() で "/" から始まるパスを指定した場合は有無を言わさずリソースが見つかりません。これはバグとしか思えませんが、ClassLoader#getResource() の検索仕様は明確に決められていないので、仕様ということもできてしまいます。
(ちなみに実装がどうなっているかというと、ディレクトリベースの場合は new URL(baseDirUrl, "/name") という処理が行われ、この結果が壊れます。jar の場合は JarEntry の取得に失敗します)

また、Class#getResource のような振る舞いを期待して ClassLoader#getResource() を呼び出してしまうと、クラスの位置ではなくクラスパスのルートからの相対になるため、謎のバグに見舞われたり、混乱を招いたりします。

これだけならまだいいのですが、アプリケーションサーバなどで独自のクラスローダを用いているものでは、先頭が "/" であっても取得できてしまったりすることがあります(orion server 等で確認)。未確認ですが、先頭の "/" がないと取得できないようなものがもし存在すると、JDK との間のポータビリティが完全に失われます。

最も安全なのは、常に Class#getResource() を用いるということです。これならクラスローダ実装の差異を吸収してくれます。
この場合、相対指定のときにどのパッケージディレクトリになるかというのが、対象の Class クラスによって変わるので、深く考えずに getClass().getResource("相対パス") と書く癖があると落とし穴にはまるかもしれませんのでそこだけはご注意を。

この整理を試みたのは、hibernate が内部的に ClassLoader#getResource() を使ってマッピングファイル読み込みを行っていることが原因で、先頭に "/" を書いていて、且つスタンドアローンでの dbunit での起動時のみマッピングファイルが読み込めないといった現象になったためです。hibernate が Class#getResource() を使っていてくれるか、あるいは JDK のバグとして仕様の明確化&Sun 実装の修正がなされれば良いわけですね。

また、おまけになりますが、ClassLoader#getResource() の場合、検索のルートディレクトリより上位にあるリソースは取得できませんが、URL#getFile() を利用すると、物理パスを取得できるので、書き出し用ディレクトリの取得もできたりします。ただし、この方法でディレクトリを見つける場合は、クラスパスに複数ディレクトリが設定されると危険であるということを念頭におく必要があります。

2009년 10월 13일 화요일

HiRDB sql

5.コメント
 /* コメント */はありますが、-- の1行コメントがありません。

6.LIKE検索の文字列連結
 なんと、LIKE検索時に、LIKE '%' || '文字列' || '%'のような
 文字列連結をするとエラーになるようです。
 よって、CONDITIONが"CONTAINS","STARTS","ENDS","NOT_CONTAINS"
 のSQLは、HiRDBではエラーになると思われます・・・

 対策としては、下記の2パターンがあると思います。
 (1)buyerItemCd_CONTAINS,buyerItemCd_STARTS,buyerItemCd_ENDS,
   buyerItemCd_NOT_CONTAINSを作成し、%をつけた文字列を設定。
   SQLファイルとしては、現状の'%'|| || '%'を削除。
 (2)複数入力ありのSQLと同様に、ジョンギルユーティリティで、
   検索条件SQLは作成してしまう。
   /*$conditionSql*/

 いずれにせよLIKE検索は、%と_のESCAPE対応が必要なので、どちらかの
 対応は必要になります。
 どうせ複数入力ありの対応をやるなら(2)でも良いかなぁと思っています。
 ユーザのロールや所属に合わせた範囲指定などだけをSQLファイルに
 指定してもらうイメージです。

【現状のSQLファイル】
/*IF buyerItemCd != null && buyerItemCd_CONDITION == "CONTAINS" */AND BUYER_ITEM_CD LIKE '%' || /*buyerItemCd*/ ||
'%'/*END*/
/*IF buyerItemCd != null && buyerItemCd_CONDITION == "STARTS" */AND BUYER_ITEM_CD LIKE '%' || /*buyerItemCd*//*END*/
/*IF buyerItemCd != null && buyerItemCd_CONDITION == "ENDS" */AND BUYER_ITEM_CD LIKE /*buyerItemCd*/ || '%'/*END*/
/*IF buyerItemCd != null && buyerItemCd_CONDITION == "NOT_CONTAINS" */AND BUYER_ITEM_CD NOT LIKE '%' || /*buyerItemCd*/
|| '%'/*END*/

 ちなみに、ESCAPE処理を検証したところ、下記の感じでよさそうです。
 LIKE '文字列' ESCAPE '|'とした場合の文字列の組み立て形。
 (1)画面入力された文字の|を||に置換
 (2)画面入力された文字の%を|%に置換
 (3)画面入力された文字の_を|_に置換
 (4)画面入力された文字の%を|%に置換
 (5)画面入力された文字の_を|_に置換
 (6)CONTAINS、STARTS、ENDS、NOT_CONTAINSの条件に合わせて、%を前後に追加
 
 例:画面の検索条件 テスト|12345→ '%テスト||12345%'
  

2009년 10월 12일 월요일

MockInterceptor








package jp.co.hnj.framework.unit;

import java.util.List;

import org.apache.poi.hssf.record.formula.functions.T;
import org.seasar.extension.dataset.DataTable;
import org.seasar.framework.exception.SRuntimeException;
import org.seasar.framework.unit.DataAccessor;
import org.seasar.framework.unit.impl.DataAccessorImpl;

/**
 * @author Administrator
 *
 */
public class MockInterceptor  extends
org.seasar.framework.aop.interceptors.MockInterceptor {

/**
* serialVersionUID
*/
private static final long serialVersionUID = 1L;

private DataAccessor accessor = new DataAccessorImpl();

private MockParser parser ;

public MockInterceptor() {
System.out.println("##");
}
public void setReturnValue(String methodName, String sheetPath, Class<T> dtoClazz) {
        DataTable dataTable = accessor.readXls(sheetPath).getTable(0);
        parser = new MockParserImpl(dtoClazz, dataTable);
        try {
            List<?> returnValue = parser.parse();
            super.setReturnValue(methodName, returnValue.get(0));
        } catch (Exception e) {
            e.printStackTrace();
            throw new SRuntimeException("");
        }
}
public void setReturnListValue(String methodName, String sheetPath, Class<T> dtoClazz) {
   DataTable dataTable = accessor.readXls(sheetPath).getTable(0);
   parser = new MockParserImpl(dtoClazz, dataTable);
   try {
List<?> returnValue = parser.parse();
super.setReturnValue(methodName, returnValue);
} catch (Exception e) {
e.printStackTrace();
throw new SRuntimeException("");
}
}
}

windows xp tip - 자동로그인

Subject

自動的にログオンする

Software

Windows XP 分類 起動/終了

 以下の設定をすれば、アカウントが複数でパスワードが設定されていても自動ログオンできます。 

 ① [スタート]ボタンをクリックする。

② [ファイル名を指定して実行]をクリックする。

 

 


③「control userpasswords2」と入力する。

④ [OK]ボタンをクリックする。


⑤ [ユーザーアカウント]ダイアログボックス
    の[ユーザーがこのコンピュータを使うには、
    ユーザー名とパスワードの入力が必要]
    ボックスのチェックを解除する。

 ⑥ [OK]ボタンを入力する。

 


⑦ [自動ログオン]ダイアログボックスに自動
     ログオンするユーザー名、パスワードを
     入力する。

 ⑧ )[OK]ボタンをクリックする。

 

 

cubby2.0

Cubby とは何ですか ?

Cubby (カビー)は Java / Servlet / JSP をベースにした、Web アプリケーションのためのシンプルなフレームワークです。

  • シンプルで小さい(薄い)フレームワークです。
  • 設定ファイルはほとんど使用しません。
  • REST っぽい URI をサポートします。 (/username/todo/1001、拡張子が付かない URI など)
  • 直感的な名前のカスタムタグライブラリ
  • HTMLテンプレートエンジン「Mayaa」と連携することができます。

Cubby のゴール

  • Servlet / JSP / Java を知っていれば使いこなせる。
  • 美しい URL を持つ Web アプリケーションをさくさく作成できる。
  • アクションクラスを見れば何をやっているのかわかる。
  • トラブルに遭遇したときに、わかりやすいメッセージで原因解決がすぐにできる。

generic java

これまで3回にわたり、J2SE 5.0で導入されたGeneric Javaについて紹介し、Generic Javaを適用して作成されたAPIを使う方法をとりあげてきた。今回からは、Generic Javaを使ってAPIを作成する方法について紹介する。

ソースコードを読む

Generic Javaプログラミングを学ぶよい方法のひとつは、コレクションフレームワーク(java.util)のソースコードを読むことにある。コレクションフレームワークのソースコードで使われているGenericは生きたGenericだ。ためしにjava.util.ArrayListのソースコードを眺めてみるとよいだろう。

これはGenericに限らず多くの場合にいえることだが、実際に使われてるソースコードから学ぶのは有効な方法だ。そこには必要であるという観点に基づいた実装がある。教材では、その機能を説明する観点からしばしば、言語規則をベースにして無理やり説明のために起したソースコードが用いられるが、これは役に立たないことが多い。使いもしない枝葉末節な規則や実装方法を見せられて、その複雑さにうんざりしてプログラミングの道から遠ざかってしまった人も多いだろう。

実際に使われているソースコードではそういった危険性が少ない。実装はできるが実際にはあまり使わないといったソースコードを見る無駄を避けることができるし、機能以外でもっと大切なことが学べる。これからGeneric Javaの実装方法を紹介していくが、基本的な機能がわかったらコレクションフレームワーク(java.util)のソースコードを見てほしい。クラス、メソッド、インタフェース、継承、実装、そういったところでどのようにGenericを使うのかがよくわかるだろう。

Generic - メソッドで使う

実装の面から学ぶ場合、もっともわかりやすい例はメソッドでGenericを使う場合だろう。メソッドでGenericを使った実装例をリスト1に、そのメソッドを使う例をリスト2に示す。

リスト1 GenericMethod.java

public class GenericMethod {
    public <T> void doNothing(T aTarget) {
        //
    }
}

リスト2 Test1.java

public class Test1 {
    public static void main(String[] anyArguments) {
        GenericMethod gm = new GenericMethod();

        gm.doNothing(new Object());
        gm.doNothing(new Integer(1));
        gm.doNothing("String");
    }
}

まず、リスト1におけるリスト2.3に注目してほしい。publicのあとに<T>という表記が加わっている。これは、このメソッドで使用するTがテンプレートだということを意味している。名前はTでもHでもなんでもよいが、慣例的にはTやEといったように大文字一文字が使われることが多い。

J2SE 1.4以前であれば、(T aTarget)の部分でTというクラスはないというエラーが表示されたコンパイルできない。J2SE 5.0以降であれば<T>という指定からTはテンプレートとして扱えるから、(T aTarget)という指定が有効になる。この記述方法はメソッドでテンプレート使う場合のもっとも基本的な記述方法だから覚えてしまおう。

リスト3 メソッドでテンプレートを使う

    public <T> void doNothing(T aTarget)

リスト2からは、メソッドの引数にそれぞれ違うクラスのインスタンスを渡していることがわかる。J2SE 1.4以前であれば、引数の種類の分だけメソッドを用意するか、メソッド側でObjectとして受け取る必要があったが、その必要がなくなっていることがわかるだろう。

また、リスト1とリスト2をそれぞれ逆コンパイルしたものをリスト4およびリスト5に示しておく。内部ではObjectで受け取るような実装に変更されていることがわかる。

リスト4 GenericMethod.javaをコンパイルして逆コンパイルしたもの

public class GenericClass
{
    private Object element;
}

リスト5 Test1.javaコンパイルして逆コンパイルしたもの

public class Test1
{
    public static void main(String args[])
    {
        GenericMethod genericmethod = new GenericMethod();

        genericmethod.doNothing(new Object());
        genericmethod.doNothing(new Integer(1));
        genericmethod.doNothing("String");
    }
}

Generic - クラスで使う

今度はクラスでGenericを使う方法を紹介する。リスト6とリスト7が実装例だ。コレクションフレームワークでは主にリスト6とリスト7のようにクラスで使われている。

リスト6 GenericClass.java

public class GenericClass<E> {
    private E element;
}

リスト7 Test2.java

public class Test2 {
    public static void main(String[] anyArguments) {
        GenericClass<String> gc = new GenericClass<String>();
    }
}

メソッドで使用するテンプレートは、メソッドの可視性のあとに使用するテンプレートを指定した。クラスの場合は、リスト8のようにクラス名の後に<E>のようにそのクラスで使用する名前を指定する。

リスト8 テンプレートの指定

public class GenericClass<E>

リスト8のように指定されていると、リスト9のようにそのクラスで使われるEはテンプレートとして使うことができるようになる。

リスト9 クラス内で使用する

    private E element;

このようにクラスを作成すると、そのクラスのインスタンスを作成するときにリスト10のようにインスタンスを生成できるようになる。前回と前々回に紹介したGenericに対応したAPIを使う時の新しい記述ができるようになるわけである。

リスト10 インスタンス生成時に使用するクラスを指定できる

        GenericClass<String> gc = new GenericClass<String>();

この記述方法も、クラスでテンプレートを扱う場合の基本的なものとなるから、覚えてしまおう。なお、リスト6とリスト7を逆コンパイルしたものをリスト11とリスト12に示しておく。メソッドの場合と同じく、実際にテンプレート部分はObjectに変更されていることがわかるだろう。

リスト11 GenericClass.javaをコンパイルして逆コンパイルしたもの

public class GenericClass
{
    private Object element;
}

リスト12 Test2.javaコンパイルして逆コンパイルしたもの

public class Test2
{
    public static void main(String args[])
    {
        GenericClass genericclass = new GenericClass();
    }
}

Genericプログラミングの基本はこのふたつだ。そのほかの書き方としては、テンプレートにおける継承の指定や、複数の値をテンプレートする場合の記述方法などがあり、機能としては細かい規則などがある。これらについては次回から重要なポイントを取り上げて紹介していく。


seasar test

presentation