ロケール対応プラグインを作ってみる(Grails 0.5)

前のページで「先日のロケール対応処理が2重に実行されそうな気がしなくもない」って書いてあったところをチェックしてみた。ええ、そりゃあもう2重に呼び出されていましたとも(威張るところではないけど)。
getResourceForUriっていう名前のとおりにロケールだとかをURIにつけちゃいけないんだろうけど、このメソッドは、org.codehaus.groovy.grails.web.pages#doPage()から呼び出されているので、ここしかないんだと思う(プラグインからGSPサーブレットのクラスを変えるとかはできないと思われ)。このメソッドのソースは、

    public void doPage(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    	request.setAttribute(GrailsApplicationAttributes.REQUEST_SCOPE_ID, grailsAttributes);

        GroovyPagesTemplateEngine engine = grailsAttributes.getPagesTemplateEngine();
        String pageName = (String)request.getAttribute(GrailsApplicationAttributes.GSP_TO_RENDER);
        if(StringUtils.isBlank(pageName)) {
            pageName = engine.getCurrentRequestUri(request);
        }

        Resource page = engine.getResourceForUri(pageName);
        if (page == null) {
            context.log("GroovyPagesServlet:  \"" + pageName + "\" not found");
            response.sendError(404, "\"" + pageName + "\" not found.");
            return;
        }

        renderPageWithEngine(engine, request, response, page);
    }

となっているので、フィルタか何かをねじ込んでGrailsApplicationAttributes.GSP_TO_RENDER属性にURIをセットすればよさそうに思えたんだけど、URIが"/"のときとかにうまく行かない...
仕方ないので、

public class LocaledViewResolver extends GrailsViewResolver {
	/** Atrribute for marking LocaledViewResolver checked resource */
	public static final String LOCALESQITCHER_CHECKED =
						"jp.ne.hatena.d.noryksj.grails.LocaleSwitchChecked";
	[...]
	/** {@inheritDoc} */
	protected View createView(String viewName, Locale locale) throws Exception {
		String prefix = getPrefix();
		String path = prefix + viewName + "_" + locale.getLanguage() + GSP_SUFFIX;
		RequestAttributes reqattrs = RequestContextHolder.getRequestAttributes();
		reqattrs.setAttribute(LOCALESQITCHER_CHECKED + "." + path, Boolean.TRUE,
						RequestAttributes.SCOPE_REQUEST);
		ResourceLoader resourceLoader = establishResourceLoader();
		Resource res = resourceLoader.getResource(path);
		if (res != null && res.exists()) {
			viewName = path.substring(prefix.length(), path.length() - GSP_SUFFIX.length());
		}
		return loadView(viewName, locale);
	}
	[...]
}

として、

public class LocaledTemplateEngine extends GroovyPagesTemplateEngine {
	/** {@inheritDoc} */
	public Resource getResourceForUri(String uri) {
		RequestAttributes reqattrs = RequestContextHolder.getRequestAttributes();
		Object done = reqattrs.getAttribute(LocaledViewResolver.LOCALESQITCHER_CHECKED + "." + uri,
						RequestAttributes.SCOPE_REQUEST);
		if (done != null) {
			return 	super.getResourceForUri(uri);
		}
		[...]
	}
}

とすることにした。これで2重チェックは防げるかな。