2

I'm setting up views in a Dropwizard app and ran into a curious issue with Freemarker.

Following the docs here I set up a very simple example as follows

public class ExampleFreemarkerView extends View {
  private Foo foo;

  public ContractHtmlView(Foo Foo) {
    super("FooView.ftl");
    this.foo = foo;
  }

  public Contract getFoo() { return foo };
}

public class Foo {
  public String bar = "Hello World";
}

With FooView.ftl

<html>
  <body>
    <h1>${foo.bar}</h1>
  </body>
</html>

Expected output when rendering ExampleFreemarkerView is an HTML document displaying Hello World.

What actually happens is Freemarker throws an exception, complaining that ${foo.bar} - specifically bar - is undefined.

This appears to be because bar is a public field, without a getter. When I add a public String getBar() { return bar; } getter to Foo, it works.

I'm somewhat surprised that this is the case - i.e. that Freemarker seems to require getters and won't work with public fields out of the box. I'm deliberately using public fields instead of getters/setters on my model objects, so adding getters just to make Freemarker work isn't a solution I'll consider.

I've googled around a lot and read through the Freemarker docs, and just can't find any way to 'turn on' this behaviour in Freemarker. Is it possible?

Just for interest - I also tried the above example, exactly the same, but with a Mustache template and public fields work fine there (i.e. {{foo.bar}} renders Hello World without issue). That solves the immediate problem, so this question is mostly just out of curiosity or in case I decide to use Freemarker over Mustache for other reasons.


Edit based on comments - I understand that Freemarker does this (insists on getters out the box) to follow the Java Beans spec, but most libraries in the Java ecosystem support public fields - Hibernate and Jackson being prominent examples - to the extent I personally view it as an equally valid standard and find libraries not supporting it out the box surprising.

davnicwil
  • 28,487
  • 16
  • 107
  • 123
  • Can you explain why *using public fields instead of getters/setters*? – Ori Marko Jun 18 '19 at 04:56
  • So firstly note that this is specifically for data classes, i.e. model objects - I find that no encapsulation or abstraction is necessary here. The only purpose of the class is to hold data - it's completely dumb and doesn't need to do any magic like validation or anything else. For such classes, I feel it's cleaner & simpler to just use public fields. [This answer](https://softwareengineering.stackexchange.com/a/261868/187440) also does a good job of explaining it in more words than I can use here. – davnicwil Jun 18 '19 at 17:18

2 Answers2

5

This all depends on the objectWrapper configuration setting. The DefaultObjectWrapper (and any BeansWrapper subclass), which most projects are using, has an exposeFields setting that can be set to true.

In Dropwizard, that can be done like this in the configuration YML, if you set up your ViewBundle in a compatible manner (based on https://github.com/apache/freemarker-online-tester):

viewRendererConfiguration:
  freemarker:  # was `.ftl:` before Dropwizard 1.3.0
    objectWrapper=DefaultObjectWrapper(2.3.28, exposeFields=true)
ddekany
  • 29,656
  • 4
  • 57
  • 64
1

It's stated in freemarker docs

Every object will be wrapped into a TemplateHashModel that will expose JavaBeans properties and methods of the object. This way, you can use model.foo in the template to invoke obj.getFoo() or obj.isFoo() methods. (Note that public fields are not visible directly; you must write a getter method for them.)

Notice that it's also follow java encapsulation concept

Also you can use framework as lombok getters automatically using only class annotation

Community
  • 1
  • 1
Ori Marko
  • 56,308
  • 23
  • 131
  • 233
  • 1
    Thanks for this answer, I probably didn't state it clearly enough in the question but I know this is the case out the box, and understand why (following the java beans spec etc). What I'm interested in is if there's a way of configuring Freemarker so that public fields *are* visible directly, or if it's some sort of explicit philosophical hard no for the library. I can't find such a statement anywhere in the docs, so just curious. – davnicwil Jun 18 '19 at 17:24