トップ 追記

ふたつの川うるおう日記

Seasar Conference 2009 Spring - 6/13(SAT), Tokyo

2009-05-04 (Mon)

_ [Windows][大学] Windows 7 RC (x86/x64) と Vista SP2 お試し

Windows 7 は正式版出てから入れようと思っていたけど、周りが入れ始めたのでVAIO Type P に Windows 7 x86 RC を、テストマシン に Windows 7 x64 RC を入れてみました。VistaベースなのでVistaのドライバでだいたい動き、動かないのはアンチウィルスソフトぐらいでした。

VAIO Type Pの方は、EMOBILE D02HWでの通信もOKでした。

テストマシンの方は、3ware 9650SEでRAID 1構成にしていたのでインストーラが3ware 9650SEを認識しなかったけど、3wareのサイトにあるドライバ(driver-win-9.5.1.zipの中身のx64)を指定すれば正しく認識されて動作OKでした。

Vistaでやや微妙だった点がいろいろ改善されてて良い感じです。それにまだ一度もエクスプローラが落ちていない(^^)。

正式にリリースされたらすぐに乗り換えて問題なさげです。来年の1年生用ノーパソも Windows 7 x64 になると思います。ついでにラボ環境も。

| Bookmark:

2009-04-16 (Thu)

_ [Java][Seasar] 入力された文字の前後の空白スペースなどの文字列を除去しても有効な文字があるか検証するValidatorと実際に取り除く処理

入力フォームでユーザに文字を入力させると、前後に空白スペースなどを入れちゃったりしますよね。 Validatorで前後に空白スペースがあればエラーにしても良いですが、それはちょっと不親切なので自動的に取り除いてあげるのが親切かなと思います。 また、String の trim() 関数だと全角スペース1文字がエラーになりません。 得てして前後にスペースを入れちゃう人は全角のスペースを入れるのでこれもチェックしないといけません。 というわけで、Cubby 1.1.x 向けにValidatorを書いてみました。

取り除きたい文字は半角・全角スペースだけとも限らないので、取り除きたい正規表現を前後別々に指定できるようになっています。引数が1個の時は前後とも同じ正規表現が使用されます。

  • RegexTrimRequiredValidator
import org.seasar.cubby.validator.MessageHelper;
import org.seasar.cubby.validator.ScalarFieldValidator;
import org.seasar.cubby.validator.ValidationContext;
import org.seasar.framework.util.StringUtil;

/**
 * 先頭と末尾を正規表現でtrimした文字列の必須検証をします。
 * <p>
 * trimした文字列の長さが0の場合、検証エラーとなります。
 * </p>
 * <p>
 * デフォルトエラーメッセージキー: valid.required
 * </p>
 *
 * @author jfut
 */
public class RegexTrimRequiredValidator implements ScalarFieldValidator {

    /** メッセージヘルパ */
    private final MessageHelper messageHelper;
    /** 文字の先頭から除去する正規表現 */
    private String prefixRegexTrim;
    /** 文字の末尾から除去する正規表現 */
    private String postfixRegexTrim;

    /** 正規表現における先頭識別文字 */
    private static final char BEGIN_CHAR = '^';
    /** 正規表現における末尾識別文字 */
    private static final char END_CHAR = '$';

    /**
     * インスタンスを作成します。
     */
    public RegexTrimRequiredValidator() {
        this(null, null);
    }

    /**
     * インスタンスを作成します。
     *
     * @param regexTrim
     *            除去する正規表現
     */
    public RegexTrimRequiredValidator(final String regexTrim) {
        this(regexTrim, regexTrim);
    }

    /**
     * インスタンスを作成します。
     *
     * @param prefixRegexTrim
     *            文字の先頭から除去する正規表現
     * @param postfixRegexTrim
     *            文字の末尾から除去する正規表現
     */
    public RegexTrimRequiredValidator(final String prefixRegexTrim,
            final String postfixRegexTrim) {
        this(prefixRegexTrim, postfixRegexTrim, "valid.required");
    }

    /**
     * エラーメッセージキーを指定してインスタンスを作成します。
     *
     * @param prefixRegexTrim
     *            文字の先頭から除去する正規表現
     * @param postfixRegexTrim
     *            文字の末尾から除去する正規表現
     * @param messageKey
     *            エラーメッセージキー
     */
    public RegexTrimRequiredValidator(final String prefixRegexTrim,
            final String postfixRegexTrim, final String messageKey) {
        this.prefixRegexTrim = prefixRegexTrim;
        this.postfixRegexTrim = postfixRegexTrim;
        this.messageHelper = new MessageHelper(messageKey);
        setupRegexTrim();
    }

    /**
     * 正規表現をセットアップします。
     */
    public void setupRegexTrim() {
        // prefixRegexTrim
        if (prefixRegexTrim != null) {
            // 文字の末尾が $ の場合、取り除きます
            if (prefixRegexTrim.charAt(prefixRegexTrim.length() - 1) == END_CHAR) {
                prefixRegexTrim =
                    prefixRegexTrim.substring(0, prefixRegexTrim.length() - 1);
            }
            // 文字の先頭に ^ が無い場合、追加します
            if (prefixRegexTrim.charAt(0) != BEGIN_CHAR) {
                prefixRegexTrim = BEGIN_CHAR + prefixRegexTrim;
            }
        } else {
            prefixRegexTrim = "";
        }
        // postfixRegexTrim
        if (postfixRegexTrim != null) {
            // 文字の先頭が ^ の場合、取り除きます
            if (postfixRegexTrim.charAt(0) == BEGIN_CHAR) {
                postfixRegexTrim =
                    postfixRegexTrim.substring(1, postfixRegexTrim.length());
            }
            // 文字の末尾に $ が無い場合、追加します
            if (postfixRegexTrim.charAt(postfixRegexTrim.length() - 1) != END_CHAR) {
                postfixRegexTrim = postfixRegexTrim + END_CHAR;
            }
        } else {
            postfixRegexTrim = "";
        }
    }

    /**
     * {@inheritDoc}
     */
    public void validate(final ValidationContext context, final Object value) {
        if (value instanceof String) {
            final String str = trim((String)value);
            if (!StringUtil.isEmpty(str)) {
                return;
            }
        } else if (value != null) {
            return;
        }
        context.addMessageInfo(this.messageHelper.createMessageInfo());
    }

    /**
     * 指定された文字列の先頭と末尾をtrimします。
     *
     * @param value
     *            文字列
     * @return trimされた文字列
     */
    public String trim(String value) {
        if (value != null) {
            value = value.replaceAll(prefixRegexTrim, "");
            value = value.replaceAll(postfixRegexTrim, "");
        }
        return value;
    }
}
  • 適当なActionでの使用例

入力値をチェックしつつ、不要な文字を取り除いても有効な文字がある時は、同じ条件で不要な文字をtrimできるようにインスタンス化して使用します。 ポイントはパラメータがバインドされたインスタンスをtrimするために、ValidationRulesでValidateしつつ、最後にインナークラスRegexTrimValidationRuleFilterでtrimを実行しておくところです。 これによりアクションメソッド実行時には既にtrimされた値が入ったインスタンスを使うことができます(参考: リクエストからアクション実行までのフロー、Action#preactionなんてのがあるとそこが適切かも?Interceptorだとちょっと書きにくいし)。

この例では、入力値の前後の半角・全角スペースをすべて取り除いても有効な文字があるかどうかをチェックし、そして、RegexTrimValidationRuleFilterで実際に不要な文字を取り除きます。

@Path("register")
public class RegisterAction extends Action {

    // -------------------------------------------------- [Validation]

    private final RegexTrimRequiredValidator trimRequiredValidator =
        new RegexTrimRequiredValidator("[\\s ]*");

    @Binding(bindingType = BindingType.NONE)
    public ValidationRules registerValidation = new DefaultValidationRules() {
        @Override
        public void initialize() {
            add("token", new TokenValidator());
            add("lastName", trimRequiredValidator);
            add("firstName", trimRequiredValidator);
            add("lastNameEnglish", trimRequiredValidator);
            add("firstNameEnglish", trimRequiredValidator);
            add("mailAddress1", trimRequiredValidator, new EmailValidator());
            add("mailAddress2", trimRequiredValidator, new EmailValidator());
            add("inside", new RequiredValidator());
            // add("uid", validator);
            add("laboratory", trimRequiredValidator);
            add("promotion", trimRequiredValidator);
            add(...他のValidationRule...);
            add(new RegexTrimValidationRuleFilter());
        }
    };

    // -------------------------------------------------- [DI Filed]

    @Resource
    private UserService userService;

    // -------------------------------------------------- [Attribute]

    protected RegisterFormDto registerFormDto;

    // -------------------------------------------------- [Action Method]

    public ActionResult index() {
        return new Forward("/register/index.html");
    }

    @Accept(POST)
    @Form("registerFormDto")
    @Validation(rules = "registerValidation", errorPage = "/register/index.html")
    public ActionResult confirm() {
        return new Forward("/register/confirm.html");
    }

    @Path("process")
    @OnSubmit("apply")
    @Accept(POST)
    @Form("registerFormDto")
    @Validation(rules = "registerValidation", errorPage = "/register/confirm.html")
    public ActionResult processApply() {
        registerFormDto.mailAddress = registerFormDto.mailAddress1;
        User user =
            Beans.createAndCopy(User.class, registerFormDto).execute();
        userService.insertAndSendMail(user);
        return new Forward("/register/success.html");
    }

    ... 他のアクションメソッド省略 ...

    // -------------------------------------------------- [Helper Method]

    // -------------------------------------------------- [Validation Class]

    private class RegexTrimValidationRuleFilter implements ValidationRule {
        public void apply(Map<String, Object[]> params, Object form,
                ActionErrors errors) {
            // BeanDesc と PropertyDesc を使って汎用的にしても良いですね
            registerFormDto.lastName =
                trimRequiredValidator.trim(registerFormDto.lastName);
            registerFormDto.firstName =
                trimRequiredValidator.trim(registerFormDto.firstName);
            registerFormDto.lastNameEnglish =
                trimRequiredValidator.trim(registerFormDto.lastNameEnglish);
            registerFormDto.firstNameEnglish =
                trimRequiredValidator.trim(registerFormDto.firstNameEnglish);
            registerFormDto.mailAddress1 =
                trimRequiredValidator.trim(registerFormDto.mailAddress1);
            registerFormDto.mailAddress2 =
                trimRequiredValidator.trim(registerFormDto.mailAddress2);
            registerFormDto.laboratory =
                trimRequiredValidator.trim(registerFormDto.laboratory);
            registerFormDto.promotion =
                trimRequiredValidator.trim(registerFormDto.promotion);
        }
    }

    ...
}
  • RegexTrimRequiredValidatorTest

テストケースも書いておきます(半角・全角スペースの違いが判り難いかも)。

public class RegexTrimRequiredValidatorTest {
    @Test
    public void test1() {
        RegexTrimRequiredValidator validator;
        validator = new RegexTrimRequiredValidator();
        assertNull(validator.trim(null));
        assertEquals("", validator.trim(""));
        assertEquals(" ", validator.trim(" "));
        assertEquals("abc", validator.trim("abc"));

        validator = new RegexTrimRequiredValidator("[\\s ]*");
        assertNull(validator.trim(null));
        assertEquals("", validator.trim(""));
        assertEquals("", validator.trim(" "));
        assertEquals("", validator.trim(" "));
        assertEquals("a b", validator.trim(" a b "));
        assertEquals("a b", validator.trim("   a b   "));
        assertEquals("abc", validator.trim("abc"));

        validator = new RegexTrimRequiredValidator("^[\\s ]*$");
        assertNull(validator.trim(null));
        assertEquals("", validator.trim(""));
        assertEquals("", validator.trim(" "));
        assertEquals("", validator.trim(" "));
        assertEquals("a b", validator.trim(" a b "));
        assertEquals("a b", validator.trim("   a b   "));
        assertEquals("abc", validator.trim("abc"));

        validator = new RegexTrimRequiredValidator("^[\\s ]*$", "^[\\s]*$");
        assertNull(validator.trim(null));
        assertEquals("", validator.trim(""));
        assertEquals("", validator.trim(" "));
        assertEquals("", validator.trim(" "));
        assertEquals("a b", validator.trim(" a b "));
        assertEquals("a b   ", validator.trim("   a b   "));
        assertEquals("abc", validator.trim("abc"));
    }
}

書いてみてなかなか便利だったので3月に書いたアプリでは大活躍でした(^^)。

[2009-04-17 14:09追記]: 日記用にActionクラスを適当に書き得てたとこがおかしかったので修正。

| Bookmark:

2009-04-09 (Thu)

_ [雑記][大学] 3月から朝から晩までモードだった

3月からずっと朝から晩までモードでしたがやっとひと段落した感じです。3月下旬ぐらいまで作ってたS2JDBC対応版Buriを使ったWEBアプリ(Cubby + S2JDBC + Buri + S2Mai + S2Directory + Mayaa)も元気に稼働中です。

3月下旬から4月頭は昔書いたS2Struts 1.1.xのWEBアプリにデータを入れるためのコンバート用プログラム書いたり、その作業のためにちょっと改修したり、その他、新学期を迎えるための雑用をひたすらこなしてました。昔書いた古いコードを触ると全部書き直したい病になるものの、今回は時間的に無理なのでそんまま使いましたが、意外と昔のままでもそれなりに効率良く書けるなーって思ったりもしました。このS2Struts使ったWEBアプリはその前はS2JSFだったので今ならCubbyで書きなおしでしょうが、数年したらまた違うかもしれないですね(^^;;。

| Bookmark:

2009-03-16 (Mon)

_ [Seasar][Project] Seasar Conference 2009 White

先週の14日(土)に Seasar Conference 2009 White が開催されました。関係者の皆様お疲れ様でした。回を重ねるごとに当日準備にもすっかり慣れてだいぶ余裕になりぶらぶらセッション見て回っていましたが、今回もたくさんの参加者があり盛り上がっていました。

また、最近触っているぶりについてid:makotanさんid:imai78さんにいろいろ質問したりも出来て良いタイミングのイベントでした。さらに懇親会でid:j5ik2oさんが取り組んでおられるぶりからS2Dao外してS2JDBCだけにするコードも少し見せていただき、既にだいぶコード書かれていて正式に動くようになるのが楽しみです!少し見せていただいたコードで、BuriPathDataビューもEntityに定義して使用されている箇所があり、なるなどなーっと思い、今日早速今書いているコードでも真似て現在の状態を取得するのに使ってみました。

懇親会は最近タイミング合わず出ていなかったのですが、久し振りに参加してid:tksmdさんとインフラまわりの話がたくさん出来て楽しかったです。既に書かれていますが、自宅鯖5台はやりすぎです(^^;;。僕はまだ物理的には2台なのできっとセーフです(^^)。HPのIPMIカード(Lights-Out 100)のRemote KVMについての話題が出ましたが、下記のページのスクリーンショットに映っているのがリモートから繋いでる画面です。

何気に Java Applet 製だったりします。IPMIカード内蔵のWEBサーバが生成するHTMLに書かれた<APPLET CODE>の<PARAM>設定で接続しようとするとIPMIカードに設定してあるローカルIPアドレス宛に接続しにいってしまうので、外部ネットワークから接続する時は、別途用意した静的HTMLファイルにSSHでポートフォワードしたポート番号を使うか、ルータのWAN側グローバルIPとNAPT用ポート番号に接続するようにして繋いでいます。仮想ドライブ機能で接続元PCにあるISOファイルから遠隔ブートしたりもできるので、これでオプション +24,150円 (今日時点だとこの値段でした) は安いです。

また、話変わって、Seasarのサーバ群も昨年末からちょっとずつ作り直しているので、メインのサーバを移行終えたらまたどこかで紹介したいと思います。

| Bookmark:
本日のツッコミ(全2件) [ツッコミを入れる]

_ tksmd [この前はお疲れ様でした。私も楽しかったです。ありがとうございました。 > 僕はまだ物理的には2台なのできっとセ..]

_ jfut [> いや、当時はまだ今ほど仮想環境の情報も少なく、気づけば物理サーバが増えていたのですよ (^^; 今は仮想環..]


2009-03-14 (Sat)

_ [雑記] 実験機材 - SSD OCZ OCZSSD2-1APX120G x 6

OCZSSD2-1APX120G x 6

SSD祭りー。タイミングの問題でVertexではなくApexです。

他の機材も揃えれば Samsung SSD Awesomeness もできるかも?

やることが溜まっているので実際触れるのは新学期始まって落ち着いてからかな。

| Bookmark:

2009-03-13 (Fri)

_ [Java][Seasar] 仮型引数を持つ関数を型引数を持つ関数でOverrideしたクラスからその関数をリフレクションで取得するとそれぞれ別の関数として見つかる

Buri + S2JDBC対応 ExampleのS2JDBCToDataAccessRule#insertでinsert関数が複数見つかる場合があったので試しにコード書いてみたらそうでした。 Genericsはコンパイル時に型が解釈されるから当然なんだろうけど、普段コンパイルエラーで生成できないバイトコードが生成されるんですね。

以下、コードと実行結果。

  • S2AbstractService
// S2-TigerのS2AbstractServiceから抜粋
public abstract class S2AbstractService<T> {
    ... 省略 ...
    public int insert(T entity) {
        return jdbcManager.insert(entity).execute();
    }
    ... 省略 ...
}
  • AbstractService
public abstract class AbstractService<ENTITY> extends S2AbstractService<ENTITY> {
    public int insert(ENTITY entity) {
        return super.insert(entity);
    }
}
  • HogeService
public class HogeService extends AbstractService<Hoge> {
    @Override
    public int insert(Hoge hoge) {
        hoge.registTime = new Timestamp(new Date().getTime());
        return super.insert(hoge);
    }
    // insert(Hoge hoge)関数とObject型が重複するので自分で書くとコンパイルエラー
    // public int insert(Object object) {
    //     ...
    // }
}
  • 出力するためのコード、@Testついてるけど何もテストはしていない
@RunWith(Seasar2.class)
@RootDicon("app.dicon")
public class HogeServiceTest {
    @Test
    public void testFindInsertMethod() {
        Method methods[] = HogeService.class.getMethods();
        for (Method method : methods) {
            if (method.getName().startsWith("insert")) {
                System.out.println("# method: " + method.toGenericString());
            }
        }
        // Overrideされた関数があるかどうかを探します
        try {
            Method method =
                ClassUtil.getMethod(
                    HogeService.class,
                    "insert",
                    new Class[] { Hoge.class });
            if (method != null) {
                System.out.println("## found: " + method.toGenericString());
            }
        } catch (NoSuchMethodRuntimeException e) {
            System.out.println("## not found");
        }
    }
}
  • 出力結果
# method: public int org.example.service.HogeService.insert(org.example.entity.Hoge)
# method: public int org.example.service.HogeService.insert(java.lang.Object)
## found: public int org.example.service.HogeService.insert(org.example.entity.Hoge)
| Bookmark:

2009-03-11 (Wed)

_ [Seasar][Java] Buri + S2JDBC対応 Example

id:imai78さん某のブログのぶり入門記マトメ , id:j5ik2oさんのBuriをS2JDBC対応にしてみる その3, id:jfluteさんのDBFlute: Buri対応のプロトタイプ公開, id:makotanさんの[buri]新たに拡張ポイント追加 に触発されて作ってみました(ほとんどid:j5ik2oさんのS2JDBCToDataAccessRuleのおかげです)。

  • Buri + S2JDBC対応 Example の buri-example4 (Eclipseプロジェクトをexportしたもの)
    • [2009-03-13 11:17追記] HogeServiceにOverrideしたinsert関数があるとinsert用OGNL式が重複するのを修正 (S2JDBCToDataAccessRule#insertSetup)

Buriの内部でS2Daoが使用されているので依存ライブラリとしての2Daoは残っていますが、とりあえず利用者からはS2Dao意識しなくてOKです。DocumentProcessorTestで実行してみた感じ、たぶんちゃんと動いてそうです。詳細は家に帰った後にやる気があればかまた明日あたり書きました。

注意として、buri-core 1.0.1-SNAPSHOTはSVNから最新のコードをチェックアウトして自分のローカル環境に mvn install してください(最新のburi-core 1.0.1-SNAPSHOTをmvn deployして欲しいかも)。

[2009-03-12 00:16追記] ファイルの説明を追加

ファイルの説明。S2JDBC-Genで自動生成させるとBuri関係のEntity、Names、Serviceも生成されますが、判りやすいようにExampleでは削除してあります。

  • src/main/java
    • entity.Document
      • S2JDBC-Genで生成したEntityクラス、publicフィールドしかありません、デフォルトのままで改変なし
    • entity.names.DocumentNames
      • S2JDBC-Genで生成したNamesクラス、このExampleでは出番ないですが実際にアプリを書くときは大活躍します、デフォルトのままで改変なし
    • service.AbstractService
      • S2JDBC-Genで生成したものにS2JDBCToDataAccessRule用に public ENTITY select(Long id) を追加してあります
    • service.DocumentService
      • S2JDBC-Genで生成したServiceクラス、デフォルトのままで改変なし、実際にアプリを書くときはここに主にDBへの様々な方法でアクセスするためのコードを追加していきます
    • org.escafe.buri.compiler.util.impl.rules.DataAccessCheckRule
      • protected void checkKeyName(BuriDataFieldType src) でS2JDBC用に javax.persistence.Id アノテーションでプライマリキーを探すようにコードを追加してあります、buri-share.dicon をいじりたくなかったので同名クラスでごまかしています・・・要課題?
    • org.escafe.buri.compiler.util.impl.rules.S2JDBCToDataAccessRule
  • src/main/resources
    • buri/dicon/buri-user.dicon
      • id:makotanさんの[buri]新たに拡張ポイント追加で追加された userDataFieldRuleSet で S2JDBCToDataAccessRule を設定、ただ、diconファイルのinclude順の問題で、<include path="buri/dicon/event.dicon" /> と ClassDefUtilImpl のコンポーネント定義を追加
    • buri/dicon/selectByPath.sql
      • BuriPathDataを参照するS2JDBC用の2Way-SQLファイル、使い方は DocumentProcessorTest に
    • 他のdiconファイル
      • 普通のSMART deploy構成、ぶりと関係ないdiconファイルも入っていますが気にしないでください
  • src/test/resources
    • DocumentProcessorTest
      • 動作テスト用のテストクラス、id:imai78さんのぶり入門記ベースです

これで今やっている期限が来週の一人プロジェクトで楽が出来そうです。

なお、BuriAutoSelectProcessorでの簡単な動作確認しかしてないので、それ以外の複雑なことをしたらどのようになるかは試してみないと判らないです(^^;;。

| Bookmark:

2009-03-08 (Sun)

_ [Seasar][Java] 寝込んで復活したらBuri周りが素敵なことになっていた

id:j5ik2oさんがBuriをS2JDBC対応に!素晴らしい!!

ParticipantのIDが反映されるように!素晴らしい!!

Buriを使ってみようか迷ってたところでしたが、使うしかないですね。

| Bookmark:

2009-03-04 (Wed)

_ [雑記] 毎回どうも綺麗じゃないっと思ってしまう

ひさびさにWEBアプリを書き始めるといつもそう感じてしまう。

そうして似たようなのをまた一から書いてしまう。

| Bookmark:

2009-03-02 (Mon)

_ [Seasar][Project] Seasar Conference 2009 Whiteのお知らせ

3月14日(土)に Seasar Conference 2009 White が開催されます。今回もたくさんのセッションがあります。奮ってご参加くださいー。

開催日: 2009年 3月 14日(土) 12:30 - 17:45 (12:00開場)
会場: 法政大学市ケ谷キャンパス 外濠校舎3F・4F
主催: 特定非営利活動法人Seasarファウンデーション
後援: 法政大学情報科学部
参加費用: 無料
| Bookmark:

| Return to page top | Vicuna CMS - WordPress Theme - Vicuna Ninja Style for tDiary |