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

kubo [E-MAIL ADDRESS DELETED]
2011年 12月 6日 (火) 18:02:58 JST


久保(jflute)です。

森本さん、こんばんは

> しかし「Seasar2徹底入門」のP97にある通り、SMART deploy対象パッケージは
> 特に何も設定しない限り、action, form, dao, dto, interceptor, serviceの
> 6つのみで、annotationパッケージはSMART deployの自動登録対象外では
> ないのでしょうか?

たぶん、HotDeploy対象という話とSmartDeploy対象の話は
厳密には別の話になるかと自分は認識しています。
SmartDeploy対象であろうとなかろうとルートパッケージ配下の
クラスは、基本的にはHotDeploy対象になると。
(明示的にdiconに登録されたりしていなければ)

> 念のためconvention.diconにも「addIgnorePackageName」を指定しているため、
> AuthクラスはSMART deploy非対象のクラスとなり、今回のような事象は発生
> しないのではないかと思っていました。

NamingConventionImpl には、isHotdeployTargetClassName() という
メソッドがあり、実装を見る限りは ignorePackageName のクラスは
false になるように思えるので、森本さんが Ignore を指定しているの
にも関わらず発生しているとあれば、確かに不思議ではあります。


2011/12/6 Shouta Morimoto <[E-MAIL ADDRESS DELETED]>:
> 竹添様
>
> お世話になります、森本です。
>
>
> 下記ご指摘ありがとうございました。
> ご指摘の通り実装を行ってみたところ、期待通りの動作が確認できました。
> 下記コードのAuth.classはリクエスト毎に同一のクラスであるのに対し、
> Methodクラス内のdeclaredAnnotations変数に入っているAuthクラスは
> リクエスト毎に別のクラスがロードされているということですね。
>
>  Auth auth = executeConfig.getMethod().getAnnotation(Auth.class);
>
>
> ただ、一点だけ気になることがございます。
>
>> アノテーションに限らず、HOT deploy対象のクラスをHOT deploy非対象の
>> クラスから触るとこのような挙動になります。触る必要がある場合はリフレクションを
>> 使用するなどして実際の型には触れないようにする必要があります。
>
> 「HOT deploy対象のクラスをHOT deploy非対象のクラスから触る」
> ということは今回の例でいうと
>
> HOT deploy対象のクラス =Authクラス                       を
> HOT deploy非対象のクラス=SUGRequestProcessorクラス        から触る。
>
> ということをおっしゃっているのだと理解しました。
>
> しかし「Seasar2徹底入門」のP97にある通り、SMART deploy対象パッケージは
> 特に何も設定しない限り、action, form, dao, dto, interceptor, serviceの
> 6つのみで、annotationパッケージはSMART deployの自動登録対象外では
> ないのでしょうか?
>
> 念のためconvention.diconにも「addIgnorePackageName」を指定しているため、
> AuthクラスはSMART deploy非対象のクラスとなり、今回のような事象は発生
> しないのではないかと思っていました。
>
>
> ちなみに
>
>> # 書籍のサンプルでは実際の型に触れないようになっているのですが、
>> # 敢えてAuthクラスを触るように変更されているのにはもしかして何か
>> # 理由がおありでしょうか?
>
> 上記に関しては「Seasar2徹底入門」のP266~P267の例に従って
> 記載したつもりだったのですが…。
> 何か根本的に勘違いしていたら大変恐縮なのですが、
> 今一度ご教示頂けますと幸いです。
>
>
> 以上、よろしくお願いいたします。
>
> (2011/12/06 16:23), Naoki Takezoe wrote:
>> 竹添です。
>>
>> 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
>>
>>
>>
>
>
> --
> 森本 將太
> e-mail: [E-MAIL ADDRESS DELETED]
> _______________________________________________
> Seasar-user mailing list
> [E-MAIL ADDRESS DELETED]
> https://ml.seasar.org/mailman/listinfo/seasar-user


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