jersey-mvc-freemarkerでfreemarkerの設定をカスタマイズ

freemarkerという結構いろんなことが出来るJavaのテンプレートエンジンがあります。
http://freemarker.org/
今悪い意味で話題のstruts2でも使われているライブラリです。
※ freemarker自体が悪いわけではないです

また、今JavaのWAFでちょっと話題のJAX-RSという仕様がありこの参照実装であるJerseyというライブラリがあります。
https://jersey.java.net/
現時点で2.7です。

JAX-RSはRESTful Web Serviceのための言語仕様ですが、Jerseyではその独自拡張としてMVCのための機能をもっています。
その機能を使えば好きなテンプレートエンジンを使うように出来るんですが、freemarkerについては最初から拡張に含まれています。
https://jersey.java.net/documentation/latest/mvc.html#d0e13644


ここからが本題ですが、
freemarkerにはいろいろ設定が出来るのですが、
jersey-mvc-freemarkerをそのまま使うと簡単に設定が出来ません。
http://freemarker.org/docs/pgui_config.html

そこでjersey-mvcが用意している拡張ポイントを使います。
デフォルトでは下記のようにfreemarker.template.Configurationインスタンスが作られます。

                final List<TemplateLoader> loaders = Lists.newArrayList();
                if (servletContext != null) {
                    loaders.add(new WebappTemplateLoader(servletContext));
                }
                loaders.add(new ClassTemplateLoader(FreemarkerViewProcessor.class, "/"));
                try {
                    loaders.add(new FileTemplateLoader(new File("/")));
                } catch (IOException e) {
                    // NOOP
                }

                // Create Factory.
                final Configuration configuration = new Configuration();
                configuration.setTemplateLoader(new MultiTemplateLoader(loaders.toArray(new TemplateLoader[loaders.size()])));
                return configuration;

github

このConfigurationインスタンスにいろいろ設定を加えたいのですが、そこで、この親クラスを見てみます。
github
すると、getTemplateObjectFactoryメソッド内(1行目)で、jerseyのconfigクラスに設定されたプロパティを見ていることがわかります。
ここでのプロパティ名は
MvcFeature.TEMPLATE_OBJECT_FACTORY + suffix = FreemarkerMvcFeature.TEMPLATE_OBJECT_FACTORY
ですので、
ResourceConfigの具象クラスで次のように設定すればOKです。

property(FreemarkerMvcFeature.TEMPLATE_OBJECT_FACTORY, value);

valueには、Configurationクラスまたは継承先クラスのインスタンスもしくはパッケージ名もしくはクラスを指定できます。
パッケージ名もしくはクラスを指定した場合はインスタンスの作成にはhk2のserviceLocatorが使用されます。これによりDIが可能になります。

ただし、今回はパラメータにServletContextを渡したいのでインスタンス化したものを渡すのは難しそうです。
ということでfreemarker.template.Configurationクラスを継承したクラスを作ってそのコンストラクタ内でFreemarkerViewProcessor内の処理を実行させるて、その上で設定を行うのが良さそうです。

ということで、作りました。
FlexibleConfiguration
GitHubリポジトリ
今回は別途Mapで設定パラメータもResourceConfigに設定できるようにしてそれを読み込むことで柔軟に設定できるようにしました。