やって・・・

今日は恐ろしく眠い。すぐに寝よう。

でも「AOPをちょっとやってみた」のと「ToStringBuilder」を使うためCommonsLangを導入した。

自作Interceptor

先に書いたが、JDBCRealmを使っていて、/schedule/* にアクセスされたものは、いったんFORM認証の画面が表示されて認証成功したら、当初のURLにいっちゃうわけである。つまり、

  • /schedule/ ならば index() が呼ばれるし、
  • /schedule/hoge ならば hoge()が呼ばれてしまう。

今までは、index() の頭にユーザネームを取得して、UserService 経由でユーザ情報(UserDto )を取得する処理を書いていた。こんな感じ。

public class ScheduleAction {

    public UserDto userDto;

    public UserService userService;

    public HttpServletRequest request;

    private static Logger logger = Logger.getLogger(ScheduleAction.class);

    @Execute(validator = false)
    public String index() {
        if (userDto.id == null) {
            Principal userPrincipal = request.getUserPrincipal();
            logger.debug("Logged User is " + userPrincipal.getName());
            User user = userService.getUserDto(userPrincipal.getName());
            Beans.copy(user, userDto).execute();
        }
    :

と、まあこんな感じ。このコードをメソッドごとに複数書くのもアレだし、管理用の画面である/manage/*にも同じコードを書くのはもっとアレなので、

  • 認証後自動的にPrincipalからユーザ情報(UserDto)を取得する

インターセプターを作ってみた。*1

public class UserDtoAutoRetrieveInterceptor extends AbstractInterceptor {

    private static final long serialVersionUID = 1L;
    
    public UserService userService;
    public UserDto userDto;
    
    protected S2Container container;
    
    public Object invoke(MethodInvocation invocation) throws Throwable {
        
        HttpServletRequest request = (HttpServletRequest) container.getExternalContext().getRequest();
        Principal userPrincipal = request.getUserPrincipal();
        
        User user = userService.getUserDto(userPrincipal.getName());
        Beans.copy(user, userDto).execute();
        
        System.out.println("###" + userDto);
        
        return invocation.proceed();
    }

    public S2Container getContainer() {
        return this.container;
    }

    public void setContainer(S2Container container) {
        this.container = container.getRoot();
    }
}

あと、app.diconにコンポーネントの定義を登録して、customizer.diconのActionのほうに

        <initMethod name="addAspectCustomizer">
            <arg>"UserDtoAutoRetrieveInterceptor"</arg>
        </initMethod>

を設定した。

実はまだAOPは勉強しはじめなので、よそ様のコードの見よう見真似でわけもわからず作っているところがある。Containerのgetter/setterは本当にいるのかどうかもよくわかっていない。それはともかくだ。インターセプターでも

    public UserService userService;
    public UserDto userDto;

をインジェクションしてくれるのには感動モノだったが、さきほどみせたActionクラスのフィールドに定義している userDtoの参照は、この自作Interceptorで取得したuserDtoの参照とは異なるようで、目論見はうまくいかなかった。

両方、DIコンテナで管理してくれてるものなんだろうけどなぁ。

ToStringBuilder

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(
            this,
            ToStringStyle.MULTI_LINE_STYLE).toString();
    }

と書くと、

kizashi.scheduler.dto.UserDto@886462[
id=1
loginId=foo
passwd=bar
lastName=yamada
firstName=taro
mailAddress=hoge@huga.huga
birthday=
createdAt=2008-06-20 22:59:19.0
updatedAt=2008-06-20 22:59:12.0
createdBy=creater
updatedBy=updater
]

と、いい感じにしてくれる。
昔、toStringをがんばって実装している人たちがいたなぁ。とくにフィールドがあほほど多いクラスのtoStringメソッドは切なかった。




今日は眠いのでここまで。

SeasarをわからずにSAStrutsを使い始めたが、Seasarの(というかDIxAOPの)勉強になってよいわ。楽しい。

*1:もしこれがアプリで認証する場合だと、Loginしているかどうかを毎回チェックするインターセプターが必要になりそう。