ロケール対応プラグインを作ってみる(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重チェックは防げるかな。