82.参考: Struts2トラブルシューティング

概要

【重要】 2012.03.18追記

Struts2には、いくつかセキュリティ上の欠陥があります。

もし新たにStruts2の使用を検討されている方には、Spring MVCか他を検討することをお勧めします。

Struts2がStruts1に比較して便利になったことを前面に出して本記事を記述してきたため、

少し憂いています。Struts2のトップの記事もお読みください。

Struts2を使用するとStruts1に比べて格段に便利になります。

また、日本語のHPも増えているので、学習に時間も掛からなくなってきています。

しかし、トラブルシューティング的な日本語の情報が少ないのではないでしょうか?

ここではいくつか調べた問題とその対処方法について記述していこうかと思っています。

最初はあまり分類せずにとにかく「情報量」を第一に書いていきます。

時間があるときに少しずつ整理していきますので、よろしくお願いいたします。

もし、この情報で何か解決しましたら嬉しいです。

Actionについての問題

NoSuchMethodErrorが発生する

org.objectweb.asm.ClassReader.accept で上記のエラーが発生する場合、ASMのバージョンが古い可能性があります。

【対策】

asmをStruts2推奨の3.3以上にしましょう。それ以前の例えば2.2.3などでは上記エラーが出ます。

ちなみに、spring2.0では、2.2.3を使用することになっているので、おそらくSpring2.0がStruts2の対象になっていないのは

このためのと思われます。

Actionを妥当性チェックできない

ModelDrivenを使用していないでしょうか?

ドキュメントによると、ModelDrivenでは妥当性チェックはActionのgetterには適用されず、getModel()に適用されるようです。

http://struts.apache.org/2.x/docs/model-driven.html

【対策】

ModelDrivenを使用する場合はモデルが中心になるので、もしActionの他のプロパティに妥当性チェックをしたい場合は目的が違う気がします。

ModelDrivenをやめましょう。

java.lang.IllegalStateExceptionが発生する

上記の例外が発生して以下のようなメッセージが出る場合について。

The action name cannot be the same as the action suffix [Action]

クラス名がActionとなっているものがある可能性があります。

無設定(ゼロコンフィグ)の場合、クラス名が"Action"のみのクラスはエラーになります。

もしかすると、Struts1のjar内のActionクラスが引っかかっている可能性があります。

【対策】

Struts1を使用しないのであれば、Struts1のjarをクラスパスから全て削除してください。

もし、ゼロコンフィグを使用しないのであれば、Struts2の設定ファイルで struts.convention.action.disableScanningをtrueにしてください。

80.参考: struts.xmlのconstantの設定値一覧

最初だけActionのメソッドが呼ばれない

プロパティ値の初期値が妥当性チェックに引っかかっていないでしょうか?

例えば、int age; というプロパティがあって、妥当性チェックでage > 20 といった設定をしている場合を考えてみてください。

初期値は age=0に設定されますが、妥当性チェックが実行されたときにage>20を満たせず、エラーになって入力画面(input)に遷移します。

Struts2では、Actionのメソッド実行前に、まず妥当性チェックが実行されるからです。

ですのでプロパティの初期値が妥当性チェックNGになる場合、Actionのメソッドが呼ばれる前に入力画面に遷移します。

【対策】

初回に呼ばれるメソッドは妥当性チェックをスキップするように@SkipValidationをつけましょう。

NoSuchMethodExceptionが発生して、指定のActionのメソッドが実行されない

Struts2のActionのメソッドはpublicのものしか実行されないようです。

ですので、実行しようとしたメソッドがprotectedやprivateなどですと上記エラーが発生します。

【対策】

実行するメソッドは、publicにしてください。

ただ、外部から実行されて困るメソッドはprotected、privateにしてください。

Stuts2は、publicのActionメソッドはすべてクライアントから実行できてしまいます。

悪意のあるユーザが操作すると実行してほしくないメソッドまで実行される可能性があります。

セキュリティホールにならないように外部から隠してください。

Invalid action class configuration ... というエラーが発生する

Spring連携し、stuts.xmlのactionタグ内のresultタグに、name="input"を明示的に記述しないと発生することがあるようです。

例外の内容: RuntimeException: Invalid action class configuration that references an unknown class named

特にドキュメントに記述がないため、もしかしましたら環境的な原因かもしれません。

【対策】

name="input"というresultタグを追記してください。

これは入力エラー時(妥当性チェックエラー時)に遷移する画面を設定するものです。

ClassCastException: XIncludeAwareParserConfigurationというエラーが発生する

eclipseで実行中にコードを書き換えると、このようなエラーが出ることがあります。

【エラーの内容】

java.lang.ClassCastException:

org.apache.xerces.parsers.XIncludeAwareParserConfiguration cannot be cast to org.apache.xerces.xni.parser.XMLParserConfiguration

以前のエラーのためにコンテキストの起動が失敗しました

これはxercesImplで必要なライブラリ(jar)が足りないために起こるようです。

【対策】

xalan.jarをクラスパスに追加してください。

型変換・妥当性チェックについて(Conversion)

数字を入力したのに、型変換でエラーが出る

数字の桁数を超えていないでしょうか?(例:1000000000000)

Struts2の数字の型変換は、例えばintの最大値を超えた場合、conversionエラーになります。

とりあえず数値に変換してくれるといったことはありません。

ですので妥当性チェックで最大値チェックを設定したところで、妥当性チェックまでたどり着きません。

【対策】

考えられる対策としては、htmlのinputタグでmaxlengthを設定して型変換エラーにしないようにすることです。

もしくは、問題のintなどのConvertionクラスを作成するという方法もあります。

空白の時にBigDecimalが型変換エラーになる

Struts2の型変換にはルールがあります。(ドキュメントのNull Property Handling(docs/type-conversion.html)を参照)

型がbeanなら、ObjectFactory#buildBean(java.lang.Class, java.util.Map)を利用して、引数なしのコンストラクタでオブジェクトを生成します。

BigDecimalの場合、引数なしのコンストラクタがないために、オブジェクトが作成できずにエラーになります。

BigIntegerについても同じ問題があります。

http://struts.apache.org/2.3.1/docs/type-conversion.html#TypeConversion-NullandBlankValues

【対策】

特にドキュメントには示されていないように見えます。

カスタムのConversionを作成して、BigInteger、BigDecimalをnullに変換できるようにしてあげれば解決します。

ちなみに、ドキュメントにはEnumへの変換もできないようなことが記述されているように見えます。

妥当性チェックのエラーメッセージにプロパティ名を出したい(プロパティがListの場合)

妥当性チェックのエラーメッセージでStruts1のようにプロパティ名を出したい場合があると思います。

出力結果例: 名前は3文字以内で入力してください。

メッセージリソースの設定例: errors.stringlength=${getText(fieldName)}は%{maxLength}文字以内で入力してください。

妥当性チェックの設定例: key="errors.stringlength"

しかし、Listになっている場合はActionクラスのgetter名は、getMembers()とか、getMemberList()にするかと思います。

そうすると、上記の「fieldName」はプロパティ名(getter名)を表しますので、getText("members")となります。

当然ながらメッセージリソースから"名前"という文字は取得できませんよね。

(getText()はメッセージリソースから引数と一致するキー名を探して、その値を取得するメソッドです)

【対策】

ひとつの解決策としては、以下のような設定をすることです。

メッセージリソースの設定例: errors.stringlength={1}は%{maxLength}文字以内で入力してください。

妥当性チェックの設定例: message=${getText("errors.stringlength", {getText("name")})}

getText(String, String[])は、メッセージリソースからメッセージを取得して、{1}などの置換文字を2番目の引数で置換します。

上記で、メッセージリソースに"name=名前"が設定されていれば、「名前は3文字以内で入力してください。」というエラーになります。

もしくは、messageにそのまま「名前は3文字以内で入力してください。」という文字列を記述しても良いと思います。

型変換で同じエラーが2回出る

妥当性チェックでvisitorを使用すると、同じエラーメッセージが2回出ることがあります。

http://grokbase.com/t/struts.apache.org/user/2008/01/conversion-error-gets-printed-twice/28jb7fdg7l4eisteht3u6fo5bvdq

どうも、visitorを設定すると、妥当性チェックのvisitorで一回、conversionError Interceptorで一回、計2回出るようです。

解決策として、モデルのメッセージリソースで、invalid.fieldvalue.<your property> = <message> を設定する方法を記述しているHPもありますが、

この方法だと、shortcircuit="true"が使えないのでうまくいかないケースがあります。

型変換に失敗すると、intなどのプロパティは値が0となります。

もし、そのプロパティで例えば「20以上の数字のみをOKにする」ような妥当性チェックを設定していると、0は妥当性チェックエラーとなりますので、

その妥当性チェックエラーと、型変換のエラー、計2つのエラーが出てしまいます。

【対策】

struts.xmlで、conversionError インターセプターをはずして、

validationに<param name="repopulateField">true</param>を追加するとうまくいきます。

【validationの記述例】

<validator type="conversion" short-circuit="true">

<param name="fieldName">age</param>

<param name="repopulateField">true</param>

<message key="errors.integer" />

</validator>

ちなみにrepopulateFieldは、intなどの型に"aa"のような入力が来て、型変換に失敗したときのための設定です。

trueにすると、元の文字列"aa"を保管することができます。

JSPのStruts2のタグでは保管された値を出力するので、型変換エラーのときにテキストボックスに"aa"という文字が出力されることになります。

参考: 04.Spring-Struts2の実際の利用サンプル(設定ファイルによる構築)

妥当性チェックのXMLで正しいDTDを指定する

実は、Struts2の妥当性チェックの機構は、前身のopensymphonyを利用しています。

ですが、このフレームワークは既に終了していまして、opensymphonyのDTDを指定するとUnknownHostExceptionが発生することがあります。

インターネットから遮断されている環境ではおそらく発生するはずです。インターネットにDTDを探しに行こうとして、接続できないからです。

以下は間違ったDTDの指定ですが、困ったことに日本語で紹介されているHPだけでなく、Struts2の本家のHPですら最新の情報に更新がされておらず

以下の記述でサンプルが書かれています。

【間違ったDTDの指定】

<!DOCTYPE validators PUBLIC

"-//OpenSymphony Group//XWork Validator 1.0.2//EN"

"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">

【対策】

Struts2用のDTDが用意されていて、それはjar内のDTDを参照するように設定されていますので、それを使いましょう。

http://issues.appfuse.org/browse/APF-516?page=com.atlassian.jira.plugin.system.issuetabpanels%3Aall-tabpanel

【妥当性チェック(validation.xml)のDTD】

<!DOCTYPE validators PUBLIC

"-//Apache Struts//XWork Validator 1.0.3//EN"

"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">

【妥当性のルール(validators.xml)のDTD】

<!DOCTYPE validators PUBLIC

"-//Apache Struts//XWork Validator Config 1.0//EN"

"http://struts.apache.org/dtds/xwork-validator-config-1.0.dtd">

※試していませんが、ドキュメントにはdoctypeはなくても動作するようなことが書かれています。

でもDTDを記述しないと、eclipseのアシスト(ctrl+space)が使用できないので、記述された方が良いかなと個人的には思います。

型変換エラー(conversion error)をログ出力したくない

型変換エラーのログ出力はちょっと五月蠅いところがあります。

運用ではこれは出力したくないですよね。

【型変換エラーのログ出力の例】

2012-02-27 00:43:37,771 WARN [com.opensymphony.xwork2.ognl.OgnlValueStack] :: Error setting expression 'user.age' with value '[Ljava.lang.String;@0a0528c8'

ognl.MethodFailedException: Method "setAge" failed for object model.User.setAge([Ljava.lang.String;)]

at ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:1292)

at ognl.OgnlRuntime.setMethodValue(OgnlRuntime.java:1481)

【対策】

http://grokbase.com/p/struts.apache.org/user/2011/05/re-help-with-methodfailedexception-in-ognl-ognlruntime-callappropriatemethod/03rtwuccu7g2nxnv2i2vaxwo3jgu

log4jのプロパティ設定をするだけで回避できます。

【log4j.properties】

# Struts OgnlUtil issues unimportant warnings

log4j.logger.com.opensymphony.xwork2.util.OgnlUtil=error

log4j.logger.com.opensymphony.xwork2.ognl.OgnlValueStack=error

Action毎に、モデルの妥当性チェック(visitor)を変更したい

【validationファイルのサンプル (アノテーションも同じことが可能です)】

<field name="user">

<field-validator type="visitor">

<param name="context">rule2</param>

<message/>

</field-validator>

</field>

contextを指定すると、モデルの妥当性チェックのファイルを変更することができるようです。

(試していませんが、ドキュメントに書いてあった内容を記述しています)

上記の場合、モデルがUserクラスであれば、"User-rule2-validation.xml" のようになります。

これを利用すれば、Actionクラス毎に妥当性チェックの動作を変えられます。

表示部分についての問題(JSP,Tilesなど)

tilesを使用したらエラーが出た

まず、Tilesのバージョンを確認してください。

現在の最新版は、2.2のようですが、2.2以降はslf4jが必要になります。

slf4jはログ出力するフレームワークで、最近ではログ出力にこれを使うことが多くなった有名なフレームワークです。

さて、問題はそのslf4jです

どうもバージョンによってはtomcatと相性が悪いようです

tomcatで使用するメソッドが他のjarに依存するようになったようです。

http://www.slf4j.org/codes.html

【対策】

解決のためには、 jcl-over-slf4j.jar を $TOMCAT_HOME/common/libに入れるように指示されています

他の解決策としては、tiles2.1を使用するという方法もあるかと思います。(上記のリンクには書いていませんが)

tiles2.1であれば、tilesの設定にワイルドカードも使用できるので、ワイルドカードを使用したい方でも大丈夫です。

Created Date: 2012/02/27