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

Shouta Morimoto [E-MAIL ADDRESS DELETED]
2011年 12月 6日 (火) 15:51:02 JST


森本と申します。
件名の件について質問させて頂きます。


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 メーリングリストの案内