Skip to content
This repository was archived by the owner on Mar 3, 2026. It is now read-only.

Commit 03dc0c9

Browse files
committed
freemarker support fix jooby-project#48
Freemarker templates for Jooby. Exposes a Configuration and [Body.Formatter]. ```xml <dependency> <groupId>org.jooby</groupId> <artifactId>jooby-ftl</artifactId> <version>{{version}}</version> </dependency> ``` It is pretty straightforward: ```java { use(new Ftl()); get("/", req {@literal ->} View.of("index", "model", new MyModel()); } ``` public/index.html: ```java ${model} ``` Templates are loaded from root of classpath: ```/``` and must end with: ```.html``` file extension. There are two ways of changing a Freemarker configuration: Just add a ```freemarker.*``` option to your ```application.conf``` file: ``` freemarker.default_encoding = UTF-8 ``` ```java { use(new Ftl().doWith((freemarker, config) -> { freemarker.setDefaultEncoding("UTF-8"); }); } ``` Keep in mind this is just an example and you don't need to set the default encoding. Default encoding is set to: ```application.charset``` which is ```UTF-8``` by default. Templates are loaded from the root of classpath and must end with ```.html```. You can change the default template location and extensions too: ```java { use(new Ftl("/", ".ftl")); } ``` Cache is OFF when ```env=dev``` (useful for template reloading), otherwise is ON. Cache is backed by Guava and default cache will expire after ```100``` entries. If ```100``` entries is not enough or you need a more advanced cache setting, just set the ```freemarker.cache``` option: ``` freemarker.cache = "expireAfterWrite=1h" ``` See {@link CacheBuilderSpec}. That's all folks! Enjoy it!!!
1 parent 58915ba commit 03dc0c9

File tree

28 files changed

+1283
-29
lines changed

28 files changed

+1283
-29
lines changed

coverage-report/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
<source>${project.parent.basedir}/jooby-servlet/src/main/java</source>
4545
<source>${project.parent.basedir}/jooby-quartz/src/main/java</source>
4646
<source>${project.parent.basedir}/jooby-jdbi/src/main/java</source>
47+
<source>${project.parent.basedir}/jooby-ftl/src/main/java</source>
4748
</sources>
4849
</configuration>
4950
</execution>
@@ -66,6 +67,7 @@
6667
<source>${project.parent.basedir}/jooby-servlet/src/test/java</source>
6768
<source>${project.parent.basedir}/jooby-quartz/src/test/java</source>
6869
<source>${project.parent.basedir}/jooby-jdbi/src/test/java</source>
70+
<source>${project.parent.basedir}/jooby-ftl/src/test/java</source>
6971
</sources>
7072
</configuration>
7173
</execution>
@@ -201,6 +203,12 @@
201203
<version>${project.version}</version>
202204
</dependency>
203205

206+
<dependency>
207+
<groupId>org.jooby</groupId>
208+
<artifactId>jooby-ftl</artifactId>
209+
<version>${project.version}</version>
210+
</dependency>
211+
204212
<!-- Servers -->
205213
<dependency>
206214
<groupId>org.jooby</groupId>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.jooby.ftl;
2+
3+
import org.jooby.View;
4+
import org.jooby.ftl.Ftl;
5+
import org.jooby.test.ServerFeature;
6+
import org.junit.Test;
7+
8+
import com.google.inject.Key;
9+
import com.google.inject.name.Names;
10+
11+
public class FtlAccessFeature extends ServerFeature {
12+
13+
{
14+
use(new Ftl());
15+
16+
get("/", req -> req.require(Key.get(View.Engine.class, Names.named("ftl"))));
17+
}
18+
19+
@Test
20+
public void access() throws Exception {
21+
request()
22+
.get("/")
23+
.expect("ftl");
24+
}
25+
26+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package org.jooby.ftl;
2+
3+
import static org.junit.Assert.assertSame;
4+
5+
import org.jooby.test.ServerFeature;
6+
import org.junit.Test;
7+
8+
import com.typesafe.config.ConfigFactory;
9+
import com.typesafe.config.ConfigValueFactory;
10+
11+
import freemarker.cache.NullCacheStorage;
12+
import freemarker.template.Configuration;
13+
14+
public class FtlCacheOffFeature extends ServerFeature {
15+
16+
{
17+
use(ConfigFactory.empty()
18+
.withValue("application.env", ConfigValueFactory.fromAnyRef("prod"))
19+
.withValue("freemarker.cache", ConfigValueFactory.fromAnyRef("")));
20+
21+
use(new Ftl());
22+
23+
get("/", req -> {
24+
assertSame(NullCacheStorage.INSTANCE, req.require(Configuration.class).getCacheStorage());
25+
return "noop";
26+
});
27+
}
28+
29+
@Test
30+
public void ftl() throws Exception {
31+
request()
32+
.get("/")
33+
.expect("noop");
34+
}
35+
36+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.jooby.ftl;
2+
3+
import org.jooby.View;
4+
import org.jooby.test.ServerFeature;
5+
import org.junit.Test;
6+
7+
public class FtlFeature extends ServerFeature {
8+
9+
{
10+
use(new Ftl());
11+
12+
get("/", req -> View.of("org/jooby/ftl/index", "model", req.param("model").value()));
13+
}
14+
15+
@Test
16+
public void freemarker() throws Exception {
17+
request()
18+
.get("/?model=jooby")
19+
.expect("<html><body>jooby</body></html>");
20+
}
21+
22+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.jooby.ftl;
2+
3+
import org.jooby.View;
4+
import org.jooby.test.ServerFeature;
5+
import org.junit.Test;
6+
7+
import com.typesafe.config.Config;
8+
9+
public class FtlLocalsFeature extends ServerFeature {
10+
11+
{
12+
use(new Ftl());
13+
14+
get("*", (req, rsp) -> {
15+
req.set("app", req.require(Config.class).getConfig("application"));
16+
req.set("attr", "x");
17+
req.set("req", req);
18+
});
19+
20+
get("/", req -> {
21+
return View.of("org/jooby/ftl/locals");
22+
});
23+
}
24+
25+
@Test
26+
public void locals() throws Exception {
27+
request()
28+
.get("/")
29+
.expect("<html><body>dev:x:x</body></html>");
30+
}
31+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.jooby.ftl;
2+
3+
import static org.junit.Assert.assertSame;
4+
5+
import org.jooby.test.ServerFeature;
6+
import org.junit.Test;
7+
8+
import freemarker.cache.NullCacheStorage;
9+
import freemarker.template.Configuration;
10+
11+
public class FtlNoCacheFeature extends ServerFeature {
12+
13+
{
14+
use(new Ftl());
15+
16+
get("/", req -> {
17+
assertSame(NullCacheStorage.INSTANCE, req.require(Configuration.class).getCacheStorage());
18+
return "noop";
19+
});
20+
}
21+
22+
@Test
23+
public void ftl() throws Exception {
24+
request()
25+
.get("/")
26+
.expect("noop");
27+
}
28+
29+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package org.jooby.ftl;
2+
3+
import static org.junit.Assert.assertTrue;
4+
5+
import org.jooby.internal.ftl.GuavaCacheStorage;
6+
import org.jooby.test.ServerFeature;
7+
import org.junit.Test;
8+
9+
import com.typesafe.config.ConfigFactory;
10+
import com.typesafe.config.ConfigValueFactory;
11+
12+
import freemarker.template.Configuration;
13+
14+
public class FtlWithCacheFeature extends ServerFeature {
15+
16+
{
17+
use(ConfigFactory.empty()
18+
.withValue("application.env", ConfigValueFactory.fromAnyRef("prod")));
19+
20+
use(new Ftl());
21+
22+
get("/", req -> {
23+
assertTrue(req.require(Configuration.class).getCacheStorage() instanceof GuavaCacheStorage);
24+
return "guava";
25+
});
26+
}
27+
28+
@Test
29+
public void hbs() throws Exception {
30+
request()
31+
.get("/")
32+
.expect("guava");
33+
}
34+
35+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.jooby.hbs;
2+
3+
import org.jooby.View;
4+
import org.jooby.test.ServerFeature;
5+
import org.junit.Test;
6+
7+
import com.typesafe.config.Config;
8+
9+
public class HbsLocalsFeature extends ServerFeature {
10+
11+
{
12+
use(new Hbs());
13+
14+
get("*", (req, rsp) -> {
15+
req.set("app", req.require(Config.class).getConfig("application"));
16+
req.set("attr", "x");
17+
req.set("req", req);
18+
});
19+
20+
get("/", req -> {
21+
return View.of("org/jooby/hbs/locals");
22+
});
23+
}
24+
25+
@Test
26+
public void locals() throws Exception {
27+
request()
28+
.get("/")
29+
.expect("<html><body>dev:x:x</body></html>");
30+
}
31+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<html><body>${model}</body></html>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<html><body>${app.env}:${attr}:${req.attr}</body></html>

0 commit comments

Comments
 (0)