I have a .ftl
file for a resource that I use to display it via a RESTful endpoint, I also want to send this representation via email to users. Dropwizard does some magic with Views that allows the populated template body to be returned to my REST endpoint (I assume as a String
or something fancier like a StringBuilder
).
Is there a way to request the body of a View
without using the rest client? I don't want to make my email representation dependent on the HTML
used in the REST client either, so no suggestions to just route the email to wget
the contents of hitting an endpoint.
From looking at the dropwizard source, it seems that I will need to acquire a ViewRenderer
from somewhere, potentially by using the ServiceLoader.load()
- as this is how ViewBundle
acquires a copy (if you don't provide any).
As pointed out in the comments, Rossiar is exactly right.
The way to achieve this is to use the same ViewRenderer
that DW is using as well. One does not have to use ServiceLocator patterns because all they do is to invoke the constructor anyway.
A little example:
public class MyView extends View {
private TestPerson person;
protected MyView(TestPerson person) {
super("TestView.ftl");
this.person = person;
}
public TestPerson getPerson() {
return person;
}
public static class TestPerson {
public String getName() {
return "PandaaDb";
}
}
}
This is the view class we want to render. Note: Freemaker expects the methods to be public, otherwise it will refuse to access them.
The corresponding ftl file:
<#-- @ftlvariable name="" type="viewTest.Two.MyView" -->
<html>
<body>
<h1>Hello, ${person.name?html}</h1>
</body>
</html>
Then, in the my main, we can do:
public class ViewTestMain {
public static void main(String[] args) throws UnsupportedEncodingException {
FreemarkerViewRenderer render = new FreemarkerViewRenderer();
TestPerson p = new TestPerson();
MyView v = new MyView(p);
ByteArrayOutputStream st = new ByteArrayOutputStream();
try {
render.render(v, Locale.getDefault(), st);
} catch (IOException e) {
e.printStackTrace();
}
String string = st.toString("UTF-8");
System.out.println(string);
}
}
Or, alternatively, we can reuse the pattern DW layed out for us by querying the service locator and checking each ViewRenderer if it is applicable.
public class ViewTestMain {
public static void main(String[] args) throws UnsupportedEncodingException {
ServiceLoader<ViewRenderer> load = ServiceLoader.load(ViewRenderer.class);
TestPerson p = new TestPerson();
MyView v = new MyView(p);
ByteArrayOutputStream st = new ByteArrayOutputStream();
ViewRenderer r = null;
for(ViewRenderer vr : load) {
if(vr.isRenderable(v)) {
r = vr;
}
}
try {
r.render(v, Locale.getDefault(), st);
} catch (IOException e) {
e.printStackTrace();
}
String string = st.toString("UTF-8");
System.out.println(string);
}
}
Note, you can add your own ServiceRenderer by simply adding the implementations to the services. This is done by placing a file with the ServiceRender interface name (fully qualified) into
src/main/resources/META-INF/services
And then adding the fully qualified implementation name into that file. This will make it discoverable.
Which will print:
<html>
<body>
<h1>Hello, PandaaDb</h1>
</body>
</html>
Now,I assume that you have your DAO/Service layer split from the jersey layer. In which case, you can write a client that simply access the same model objects, instantiates the views the same way DW is usually doing it, and simply run them through the renders.
Hope that helps,
Artur