Skip to content

Commit

Permalink
Update Freemarker and support multiple extensions for views (#2196) (#…
Browse files Browse the repository at this point in the history
…2213)

* Update Freemarker to 2.3.27-incubating

'-incubating' is just because it's now an incubating Apache project and
has nothing to do with the stability of the version.

* Renamed `ViewRenderer::getSuffix` to `getConfigurationKey`

This will remove ambiguity when multiple file formats for the same
renderer type are implemented (ex: Freemarker supports .ftl as well as
.fltx and .flth).

Keys in the views configuration are now `freemarker` instead of `.ftl`
and `mustache` instead of `.mustache`.

* Use regexes to match view renderers

This allows us to match any valid Freemarker extension (.ftl, .ftlx, or
.ftlh) as well as matching "double extension" (ex: .ftl.html).
  • Loading branch information
FredDeschenes authored and arteam committed Nov 19, 2017
1 parent 4a82069 commit 7f7b855
Show file tree
Hide file tree
Showing 12 changed files with 31 additions and 26 deletions.
10 changes: 6 additions & 4 deletions docs/source/manual/views.rst
Expand Up @@ -32,13 +32,15 @@ You can pass configuration through to view renderers by overriding ``getViewConf
}); });
} }
The returned map should have, for each extension (such as ``.ftl``), a ``Map<String, String>`` describing how to configure the renderer. Specific keys and their meanings can be found in the FreeMarker and Mustache documentation: The returned map should have, for each renderer (such as ``freemarker`` or ``mustache``), a ``Map<String, String>`` describing how to configure the renderer. Specific keys and their meanings can be found in the FreeMarker and Mustache documentation:


.. code-block:: yaml .. code-block:: yaml
views: views:
.ftl: freemarker:
strict_syntax: yes strict_syntax: true
mustache:
cache: false
Then, in your :ref:`resource method <man-core-resources>`, add a ``View`` class: Then, in your :ref:`resource method <man-core-resources>`, add a ``View`` class:


Expand All @@ -61,7 +63,7 @@ Then, in your :ref:`resource method <man-core-resources>`, add a ``View`` class:
``com.example.service.PersonView``, Dropwizard would then look for the file ``com.example.service.PersonView``, Dropwizard would then look for the file
``src/main/resources/com/example/service/person.ftl``. ``src/main/resources/com/example/service/person.ftl``.


If your template ends with ``.ftl``, it'll be interpreted as a FreeMarker_ template. If it ends with If your template path contains ``.ftl``, ``.flth``, or ``.ftlx``, it'll be interpreted as a FreeMarker_ template. If it contains
``.mustache``, it'll be interpreted as a Mustache template. ``.mustache``, it'll be interpreted as a Mustache template.


.. tip:: .. tip::
Expand Down
2 changes: 1 addition & 1 deletion dropwizard-bom/pom.xml
Expand Up @@ -196,7 +196,7 @@
<dependency> <dependency>
<groupId>org.freemarker</groupId> <groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId> <artifactId>freemarker</artifactId>
<version>2.3.23</version> <version>2.3.27-incubating</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.jdbi</groupId> <groupId>org.jdbi</groupId>
Expand Down
4 changes: 2 additions & 2 deletions dropwizard-example/example.yml
Expand Up @@ -78,9 +78,9 @@ logging:
timeZone: UTC timeZone: UTC
maxFileSize: 10MB maxFileSize: 10MB


# the key needs to match the suffix of the renderer # the key needs to match the configuration key of the renderer (ViewRenderer::getConfigurationKey)
viewRendererConfiguration: viewRendererConfiguration:
.ftl: freemarker:
strict_syntax: yes strict_syntax: yes
whitespace_stripping: yes whitespace_stripping: yes


Expand Down
Expand Up @@ -19,12 +19,13 @@
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.regex.Pattern;


/** /**
* A {@link ViewRenderer} which renders Freemarker ({@code .ftl}) templates. * A {@link ViewRenderer} which renders Freemarker ({@code .ftl, .ftlh or .ftlx}) templates.
*/ */
public class FreemarkerViewRenderer implements ViewRenderer { public class FreemarkerViewRenderer implements ViewRenderer {

private static final Pattern FILE_PATTERN = Pattern.compile("\\.ftl[hx]?");
private static final Version FREEMARKER_VERSION = Configuration.getVersion(); private static final Version FREEMARKER_VERSION = Configuration.getVersion();
private final TemplateLoader loader; private final TemplateLoader loader;


Expand Down Expand Up @@ -59,7 +60,7 @@ public FreemarkerViewRenderer() {


@Override @Override
public boolean isRenderable(View view) { public boolean isRenderable(View view) {
return view.getTemplateName().endsWith(getSuffix()); return FILE_PATTERN.matcher(view.getTemplateName()).find();
} }


@Override @Override
Expand All @@ -82,7 +83,7 @@ public void configure(Map<String, String> baseConfig) {
} }


@Override @Override
public String getSuffix() { public String getConfigurationKey() {
return ".ftl"; return "freemarker";
} }
} }
Expand Up @@ -6,7 +6,7 @@ public class AbsoluteView extends View {
private final String name; private final String name;


public AbsoluteView(String name) { public AbsoluteView(String name) {
super("/example.ftl"); super("/example.ftlh");
this.name = name; this.name = name;
} }


Expand Down
Expand Up @@ -4,6 +4,6 @@


public class ErrorView extends View { public class ErrorView extends View {
protected ErrorView() { protected ErrorView() {
super("/example-error.ftl"); super("/example-error.ftlx");
} }
} }
@@ -1,2 +1,2 @@
<#-- @ftlvariable name="" type="io.dropwizard.views.freemarker.AbsoluteView" --> <#-- @ftlvariable name="" type="io.dropwizard.views.freemarker.AbsoluteView" -->
Woop woop. ${name?html} Woop woop. ${name}
Expand Up @@ -21,11 +21,13 @@
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.regex.Pattern;


/** /**
* A {@link ViewRenderer} which renders Mustache ({@code .mustache}) templates. * A {@link ViewRenderer} which renders Mustache ({@code .mustache}) templates.
*/ */
public class MustacheViewRenderer implements ViewRenderer { public class MustacheViewRenderer implements ViewRenderer {
private static final Pattern FILE_PATTERN = Pattern.compile("\\.mustache");
private final LoadingCache<Class<? extends View>, MustacheFactory> factories; private final LoadingCache<Class<? extends View>, MustacheFactory> factories;
private boolean useCache = true; private boolean useCache = true;
private Optional<File> fileRoot = Optional.empty(); private Optional<File> fileRoot = Optional.empty();
Expand All @@ -41,7 +43,7 @@ public MustacheFactory load(Class<? extends View> key) throws Exception {


@Override @Override
public boolean isRenderable(View view) { public boolean isRenderable(View view) {
return view.getTemplateName().endsWith(getSuffix()); return FILE_PATTERN.matcher(view.getTemplateName()).find();
} }


@Override @Override
Expand Down Expand Up @@ -71,8 +73,8 @@ boolean isUseCache() {
} }


@Override @Override
public String getSuffix() { public String getConfigurationKey() {
return ".mustache"; return "mustache";
} }


private MustacheFactory createNewMustacheFactory(Class<? extends View> key) { private MustacheFactory createNewMustacheFactory(Class<? extends View> key) {
Expand Down
Expand Up @@ -41,7 +41,7 @@
* } * }
* </code></pre> * </code></pre>
* *
*<p>The {@code "profile.ftl"} or {@code "profile.mustache"} is the path of the template relative to the class name. If *<p>The {@code "profile.ftl[hx]"} or {@code "profile.mustache"} is the path of the template relative to the class name. If
* this class was {@code com.example.application.PersonView}, Freemarker or Mustache would then look for the file * this class was {@code com.example.application.PersonView}, Freemarker or Mustache would then look for the file
* {@code src/main/resources/com/example/application/profile.ftl} or {@code * {@code src/main/resources/com/example/application/profile.ftl} or {@code
* src/main/resources/com/example/application/profile.mustache} respectively. If the template path * src/main/resources/com/example/application/profile.mustache} respectively. If the template path
Expand Down Expand Up @@ -109,7 +109,7 @@ public Map<String, Map<String, String>> getViewConfiguration(T configuration) {
public void run(T configuration, Environment environment) throws Exception { public void run(T configuration, Environment environment) throws Exception {
final Map<String, Map<String, String>> options = getViewConfiguration(configuration); final Map<String, Map<String, String>> options = getViewConfiguration(configuration);
for (ViewRenderer viewRenderer : viewRenderers) { for (ViewRenderer viewRenderer : viewRenderers) {
final Map<String, String> viewOptions = options.get(viewRenderer.getSuffix()); final Map<String, String> viewOptions = options.get(viewRenderer.getConfigurationKey());
viewRenderer.configure(firstNonNull(viewOptions, Collections.emptyMap())); viewRenderer.configure(firstNonNull(viewOptions, Collections.emptyMap()));
} }
environment.jersey().register(new ViewMessageBodyWriter(environment.metrics(), viewRenderers)); environment.jersey().register(new ViewMessageBodyWriter(environment.metrics(), viewRenderers));
Expand Down
Expand Up @@ -39,7 +39,7 @@ void render(View view,
void configure(Map<String, String> options); void configure(Map<String, String> options);


/** /**
* @return the suffix of the template type, e.g '.ftl', '.mustache' * @return the key to use in the view configurations, i.e. 'freemarker' or 'mustache'.
*/ */
String getSuffix(); String getConfigurationKey();
} }
Expand Up @@ -60,10 +60,10 @@ public void addsTheViewMessageBodyWriterToTheEnvironment() throws Exception {


@Test @Test
public void addsTheViewMessageBodyWriterWithSingleViewRendererToTheEnvironment() throws Exception { public void addsTheViewMessageBodyWriterWithSingleViewRendererToTheEnvironment() throws Exception {
final String viewSuffix = ".ftl"; final String configurationKey = "freemarker";
final String testKey = "testKey"; final String testKey = "testKey";
final Map<String, String> freeMarkerConfig = Collections.singletonMap(testKey, "yes"); final Map<String, String> freeMarkerConfig = Collections.singletonMap(testKey, "yes");
final Map<String, Map<String, String>> viewRendererConfig = Collections.singletonMap(viewSuffix, freeMarkerConfig); final Map<String, Map<String, String>> viewRendererConfig = Collections.singletonMap(configurationKey, freeMarkerConfig);


final MyConfiguration myConfiguration = new MyConfiguration(); final MyConfiguration myConfiguration = new MyConfiguration();
myConfiguration.setViewRendererConfiguration(viewRendererConfig); myConfiguration.setViewRendererConfiguration(viewRendererConfig);
Expand All @@ -85,8 +85,8 @@ public void configure(Map<String, String> options) {
} }


@Override @Override
public String getSuffix() { public String getConfigurationKey() {
return viewSuffix; return configurationKey;
} }
}; };


Expand Down

0 comments on commit 7f7b855

Please sign in to comment.