[Seasar-user:21186] Re: HotDeployモードでMethodクラスからアノテーションが取得できない

Naoki Takezoe [E-MAIL ADDRESS DELETED]
2011年 12月 6日 (火) 16:23:04 JST


竹添です。

Seasar2徹底入門を参照いただいているとのこと、ありがとうございます^^

リクエストプロセッサでアクションに付与されたアノテーションの型に直接
触っているのが原因ではないでしょうか。Seasar2徹底入門の6.5.2章
P263のサンプルコードにあるように文字列でアノテーション名と比較する
などして直接Authアノテーションの型に触れないようにしてみてください。

# 書籍のサンプルでは実際の型に触れないようになっているのですが、
# 敢えてAuthクラスを触るように変更されているのにはもしかして何か
# 理由がおありでしょうか?

二度目のリクエストではリクエストプロセッサが参照するAuthはそのままで、
アクションクラスは毎回リロードされるため、リクエストプロセッサが参照する
Authと、アクションクラスに付与されているAuthは異なる型になります。

アノテーションに限らず、HOT deploy対象のクラスをHOT deploy非対象の
クラスから触るとこのような挙動になります。触る必要がある場合はリフレクションを
使用するなどして実際の型には触れないようにする必要があります。

2011年12月6日15:51 Shouta Morimoto <[E-MAIL ADDRESS DELETED]>:
> 森本と申します。
> 件名の件について質問させて頂きます。
>
>
> Webアプリケーションの認証処理をRequestProcessorを拡張することで
> 実現しようとしています。
> (「Seasar 2 徹底入門」の6.4.4章を参照しています。)
>
> 具体的には認証が必要なメソッドに@Authアノテーションを付与し、
> RequestProcessorで要求されたメソッドに@Authアノテーションが
> 付与されているかをチェックする。付与されていた場合は認証
> 画面へフォワードするという処理を実装しました。
>
> 本方式で実装したところ、CoolDeployモードでは想定通りの動作をします。
> しかしHotDeployモードでは、tomcat起動後の1回目のリクエストでは
> 想定通りの動作をするのですが、2回目以降の動作ではRequestProcessor
> の処理で@Authアノテーションを拾えなくなってしまいます。
>
> 具体的なコードと設定を下記に示させて頂きます。
>
> ■====================ここから====================■
>
> ■<rootパッケージ名>.action.test.AuthTestAction
>  ⇒ユーザ認証を必要とするメソッドを持つアクションクラス
> ------------------------------------------------------------
> package <rootパッケージ名>.action.test;
>
> import javax.annotation.Resource;
>
> import org.seasar.struts.annotation.Execute;
>
> import <rootパッケージ名>.annotation.Auth;
> import <rootパッケージ名>.dto.UserDto;
>
> public class AuthTestAction {
>  @Resource
>  protected UserDto userDto;
>
>  @Execute(validator = false)
>  @Auth
>  public String index() {
>    if (userDto.user == null) {
>      System.out.println("userDto.user is null!!");
>    } else {
>      System.out.println("userDto.user is not null, id = " +
> userDto.user.id);
>    }
>    return null;
>  }
> }
> ------------------------------------------------------------
>
> ■<rootパッケージ名>.annotation
> ------------------------------------------------------------
> package <rootパッケージ名>.annotation;
>
> import java.lang.annotation.Documented;
> import java.lang.annotation.ElementType;
> import java.lang.annotation.Retention;
> import java.lang.annotation.RetentionPolicy;
> import java.lang.annotation.Target;
>
> @Retention(RetentionPolicy.RUNTIME)
> @Target(ElementType.METHOD)
> @Documented
> public @interface Auth {
>
> }
> ------------------------------------------------------------
>
> ■<rootパッケージ名>.dto.UserDto
>  ⇒認証済みユーザ情報を格納しておくクラス
> ------------------------------------------------------------
> package <rootパッケージ名>.dto;
>
> import java.io.Serializable;
>
> import org.seasar.framework.container.annotation.tiger.Component;
> import org.seasar.framework.container.annotation.tiger.InstanceType;
>
> import <rootパッケージ名>.entity.User;
>
> @Component(instance = InstanceType.SESSION)
> public class UserDto implements Serializable {
>  private static final long serialVersionUID = 1L;
>
>  public User user = null;
>
>  public boolean isLogin() {
>    if (user != null) {
>      return true;
>    }
>    return false;
>  }
> }
> ------------------------------------------------------------
>
> ■<rootパッケージ名>.SUGRequestProcessor
>  ⇒S2RequestProcessorを拡張した独自クラス
> ------------------------------------------------------------
> package <rootパッケージ名>;
>
> import java.io.IOException;
>
> import javax.servlet.ServletException;
> import javax.servlet.http.HttpServletRequest;
> import javax.servlet.http.HttpServletResponse;
>
> import org.apache.struts.action.Action;
> import org.apache.struts.action.ActionForm;
> import org.apache.struts.action.ActionForward;
> import org.apache.struts.action.ActionMapping;
> import org.apache.struts.action.InvalidCancelException;
> import org.seasar.framework.container.SingletonS2Container;
> import org.seasar.struts.action.S2RequestProcessor;
> import org.seasar.struts.config.S2ExecuteConfig;
> import org.seasar.struts.util.S2ExecuteConfigUtil;
> import org.seasar.struts.util.URLEncoderUtil;
>
> import <rootパッケージ名>.annotation.Auth;
> import <rootパッケージ名>.dto.UserDto;
> import <rootパッケージ名>.exception.AuthException;
> import <rootパッケージ名>.util.SUGConf;
>
> public class SUGRequestProcessor extends S2RequestProcessor {
>  @Override
>  public void process(HttpServletRequest request, HttpServletResponse
> response)
>      throws IOException, ServletException {
>    request = processMultipart(request);
>    String path = processPath(request, response);
>    if (path == null) {
>      return;
>    }
>    processLocale(request, response);
>    processContent(request, response);
>    processNoCache(request, response);
>    if (!processPreprocess(request, response)) {
>      return;
>    }
>    processCachedMessages(request, response);
>    ActionMapping mapping = processMapping(request, response, path);
>    if (mapping == null) {
>      return;
>    }
>    ActionForm form = processActionForm(request, response, mapping);
>    processPopulate(request, response, form, mapping);
>
>    try {
>      if (!processRoles(request, response, mapping)) {
>        return;
>      }
>    } catch (AuthException ae) {
>      // ログインページにリダイレクトする前にリクエストされたURLをクエリ
> ストリングに保持
>      String requestedURL = (String) request
>          .getAttribute("javax.servlet.forward.servlet_path");
>      if (request.getAttribute("javax.servlet.forward.query_string") !=
> null) {
>        requestedURL += "?"
>            + (String) request
>                .getAttribute("javax.servlet.forward.query_string");
>      }
>
>      String redirectURL = SUGConf.loginURL + "?" + SUGConf.requestedURLStr
>          + "=" + URLEncoderUtil.encode(requestedURL);
>
>      // ログインページにリダイレクトさせる
>      ActionForward forward = new ActionForward(redirectURL, true);
>      processForwardConfig(request, response, forward);
>      return;
>    }
>    try {
>      if (!processValidate(request, response, form, mapping)) {
>        return;
>      }
>    } catch (InvalidCancelException e) {
>      ActionForward forward = processException(request, response, e, form,
>          mapping);
>      processForwardConfig(request, response, forward);
>      return;
>    } catch (IOException e) {
>      throw e;
>    } catch (ServletException e) {
>      throw e;
>    }
>    if (!processForward(request, response, mapping)) {
>      return;
>    }
>    if (!processInclude(request, response, mapping)) {
>      return;
>    }
>    Action action = processActionCreate(request, response, mapping);
>    if (action == null) {
>      return;
>    }
>    ActionForward forward = processActionPerform(request, response, action,
>        form, mapping);
>    processForwardConfig(request, response, forward);
>
>  }
>
>  @Override
>  protected boolean processRoles(HttpServletRequest request,
>      HttpServletResponse response, ActionMapping mapping) throws
> IOException,
>      ServletException {
>
>    S2ExecuteConfig executeConfig = S2ExecuteConfigUtil.getExecuteConfig();
>    Auth auth = executeConfig.getMethod().getAnnotation(Auth.class);
>
>    if (auth != null) {
>      UserDto userDto = SingletonS2Container.getComponent(UserDto.class);
>
>      boolean isLogin = userDto.isLogin();
>      if (!isLogin) {
>        // 認証例外を発生させ、ログインページにリダイレクトさせる
>        throw new AuthException();
>      }
>    }
>
>    return super.processRoles(request, response, mapping);
>  }
> }
> ------------------------------------------------------------
>
> ■convention.dicon
> ------------------------------------------------------------
> <?xml version="1.0" encoding="UTF-8"?>
> <!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
>        "http://www.seasar.org/dtd/components24.dtd">
> <components>
>        <component
> class="org.seasar.framework.convention.impl.NamingConventionImpl">
>                <initMethod name="addRootPackageName">
>                        <arg>"<rootパッケージ名>"</arg>
>                </initMethod>
>                <initMethod name="addIgnorePackageName">
>                        <arg>"<rootパッケージ名>.dto"</arg>
>                </initMethod>
>                <initMethod name="addIgnorePackageName">
>                        <arg>"<rootパッケージ名>.annotation"</arg>
>                </initMethod>
>        </component>
>        <component
> class="org.seasar.framework.convention.impl.PersistenceConventionImpl"/>
> </components>
> ------------------------------------------------------------
>
> ■app.dicon
> ------------------------------------------------------------
> <?xml version="1.0" encoding="UTF-8"?>
> <!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
>        "http://www.seasar.org/dtd/components24.dtd">
> <components>
>  <include path="convention.dicon"/>
>  <include path="aop.dicon"/>
>  <include path="j2ee.dicon"/>
>  <include path="s2jdbc.dicon"/>
>  <component name="actionMessagesThrowsInterceptor"
> class="org.seasar.struts.interceptor.ActionMessagesThrowsInterceptor"/>
>
>  <!-- 自作コンポーネントの登録 -->
>  <component name="userDto" class="<rootパッケージ名>.dto.UserDto"
> instance="session"/>
> </components>
> ------------------------------------------------------------
>
> ■====================ここまで====================■
>
>
>
> デバッグモードで確認してみると、HotDeployモード時のtomcat起動後2回目
> 以降のアクセスだと、SUGRequestProcessorクラスの以下の行にて、
> getAnnotationメソッドの返り値がnullとなってしまうようです。
>
>  Auth auth = executeConfig.getMethod().getAnnotation(Auth.class);
>
> もう少し詳しく見てみると、java.lang.reflect.Methodクラスにて、
> Map型のdeclaredAnnotations変数からAuth.classを引数にして
> getメソッドを呼んだ際に、declaredAnnotations変数にはAuthアノテーション
> が入っているにも関わらず、引数のAuthクラスとはhash値が異なるため
> mapからの取得ができていないようです。
>
>
> 以上、恐れ入りますが原因がお分かりの方がいらっしゃれば
> お教え頂ければ幸いです。
>
> ■開発環境
> OS:Windows 7 Home Premium (64bit)
> ブラウザ: Firefox 8.0
> DB: mysql5.5
> コンパイラ: jdk1.6.0_22
> 使用中のライブラリ:(Jarを一部抜粋)
>  - sa-struts-1.0.4-sp8.jar
>  - struts-1.2.9.jar
>  - mysql-connector-java-5.1.14-bin.jar
>  - s2-framework-2.4.41.jar
>  - s2-tiger-2.4.41.jar
>  - s2-extension-2.4.41.jar
>  - s2jdbc-gen-2.4.41.jar
> _______________________________________________
> Seasar-user mailing list
> [E-MAIL ADDRESS DELETED]
> https://ml.seasar.org/mailman/listinfo/seasar-user



-- 
Naoki Takezoe


Seasar-user メーリングリストの案内