タグ「Struts2」が付けられているもの

前の記事でIncludeクラスのエンコード設定がおかしいため、JspTemplateEngineで文字化けが発生する問題を取り上げましたが、今回はそれを回避するコードを書いてみました。
IncludeクラスのInstanceを1回作成し、setDefaultEncodingを実行すればstaticなフィールドにデフォルトエンコードが設定されるのでContextListenerの中でそういったコードを書きます。

ContextListenerの中でDispatcherへListenerを追加します。そしてそのListenerの中で初期化処理を行います。なぜこういう風にするのかと言うと、Struts2のコントローラーはFilterで実装されています。ContextListenerで直接初期化処理を書いてしまうと、Filterの初期化よりも前に実行されてしまい、Struts2の初期化処理が行われていない状態で実行されてしまう事になるからです。

 

public class DefaultContextListener implements ServletContextListener {
	private DispatcherListener dispatchListener = new DispatcherListener() {

		private ValueStackFactory valueStackFactory;
		private String defaultEncoding;

		@SuppressWarnings("unused")
		@Inject
		public void setValueStackFactory(ValueStackFactory valueStackFactory) {
			this.valueStackFactory = valueStackFactory;
		}

		@Inject(StrutsConstants.STRUTS_I18N_ENCODING)
		@SuppressWarnings("unused")
		public void setDefaultEncoding(String val) {
			defaultEncoding = val;
		}

		@Override
		public void dispatcherInitialized(Dispatcher du) {
			du.getContainer().inject(this);
			ValueStack stack = valueStackFactory.createValueStack();
			new Include(stack, null, null).setDefaultEncoding(defaultEncoding);
		}

		@Override
		public void dispatcherDestroyed(Dispatcher du) {

		}
	};

	@Override
	public void contextInitialized(ServletContextEvent sce) {

		Dispatcher.addDispatcherListener(dispatchListener);

	}

	@Override
	public void contextDestroyed(ServletContextEvent sce) {

		Dispatcher.removeDispatcherListener(dispatchListener);

	}

}

 

JspTemplateEngineのBug(Struts2.1.6)

結論から言うと、JSPを使ったカスタムタグを作るときにEncodingの指定を読み込めず(システムのデフォルトの文字コード)を使うようになって文字化けが発生するというバグが存在しました。

Struts2.1.6でカスタムタグを作るときにデフォルトではFreeMarkerを使うようになっていますが、VelocityやJSPもテンプレートとして使うことができます。struts.xmlでデフォルトのエンジンを変更するか、自作のカスタムタグでテンプレート名を明示的に拡張子付きで指定してあげます。(例:hogehoge.jsp)

さて、JspTemplateEngineはRequestDispatcherのIncludeを使って実装されています。
このIncludeをする部分はカスタムタグのInclude(<s:include>)のstaticなメソッドであるincludeを呼んでいます。

ソースをひもといていくと、、、

private static String defaultEncoding;
@Inject(StrutsConstants.STRUTS_I18N_ENCODING)
public void setDefaultEncoding(String encoding) {
    defaultEncoding = encoding;
}

Σ(´Д`lll)エエ!!

しかも、defaultEncodingとは別にencodingってフィールドも持っているのですがこれもstaticなわけです。
これはひどい実装だ・・。 分けている意味が分からない。。。defaultがstaticでencodingがnon-staticだったら意味はわかるのですが・・・。

しかも、struts-default.xmlのBeanの定義には↓こう書いてある。

<bean class="org.apache.struts2.components.Include" static="true" />

staticなinjectionをしますよと書いてあるのにかかわらず、Setterはstaticではないのです!!
誰がこんなコードを何のために修正したんだろうと思ってレポジトリ追ってみた。

http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/Include.java?r1=554226&r2=577728

ここで変更されたみたい。

 

ちなみに、先に<s:include>タグを実行すると、インスタンス化されて普通に@Injectが走ってstaticフィールドに設定されて以降はその文字コードを使うようにします。WTPだとデフォルトでfile.encoding=UTF-8をつけるみたいで気がつきませんでした。

(○口○*) はぁ・・