diff --git a/pom.xml b/pom.xml
index 3aeed51a6..b74b5d507 100644
--- a/pom.xml
+++ b/pom.xml
@@ -156,6 +156,7 @@
symphony-bdk-core-invokers
symphony-bdk-examples
symphony-bdk-spring
+ symphony-bdk-template
diff --git a/symphony-bdk-bom/pom.xml b/symphony-bdk-bom/pom.xml
index 123dc7494..4d6e0888d 100644
--- a/symphony-bdk-bom/pom.xml
+++ b/symphony-bdk-bom/pom.xml
@@ -67,6 +67,16 @@
symphony-bdk-core-spring-boot-starter
${project.version}
+
+ com.symphony.platformsolutions
+ symphony-bdk-template-api
+ ${project.version}
+
+
+ com.symphony.platformsolutions
+ symphony-bdk-template-freemarker
+ ${project.version}
+
diff --git a/symphony-bdk-examples/bdk-template-examples/pom.xml b/symphony-bdk-examples/bdk-template-examples/pom.xml
new file mode 100644
index 000000000..e21f1bea3
--- /dev/null
+++ b/symphony-bdk-examples/bdk-template-examples/pom.xml
@@ -0,0 +1,49 @@
+
+
+
+ symphony-bdk-examples
+ com.symphony.platformsolutions
+ 1.2.0-SNAPSHOT
+
+ 4.0.0
+
+ symphony-bdk-template-examples
+ Symphony Java BDK Examples - Template
+ Symphony Java BDK Examples for the Template module
+
+
+
+
+ com.symphony.platformsolutions
+ symphony-bdk-bom
+ ${project.version}
+ pom
+ import
+
+
+
+
+
+
+ com.symphony.platformsolutions
+ symphony-bdk-template-freemarker
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+
+
+
diff --git a/symphony-bdk-examples/bdk-template-examples/src/main/java/com/symphony/template/examples/FreeMarkerExample.java b/symphony-bdk-examples/bdk-template-examples/src/main/java/com/symphony/template/examples/FreeMarkerExample.java
new file mode 100644
index 000000000..11ea433d0
--- /dev/null
+++ b/symphony-bdk-examples/bdk-template-examples/src/main/java/com/symphony/template/examples/FreeMarkerExample.java
@@ -0,0 +1,37 @@
+package com.symphony.template.examples;
+
+import com.symphony.bdk.template.api.Template;
+import com.symphony.bdk.template.api.TemplateEngine;
+import com.symphony.bdk.template.api.TemplateException;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.HashMap;
+
+/**
+ * Sample class showing usage of FreeMarker templates.
+ */
+@Slf4j
+public class FreeMarkerExample {
+ public static void main(String[] args) throws TemplateException {
+ log.info("String generated from built-in template: {}", getStringFromBuiltInTemplate());
+ log.info("String generated from custom template: {}", getStringFromCustomTemplate());
+ }
+
+ private static String getStringFromBuiltInTemplate() throws TemplateException {
+ Template template = TemplateEngine.getDefaultImplementation().newBuiltInTemplate("simpleMML");
+ final String message = template.process(new HashMap() {{
+ put("message", "Hello World !");
+ }});
+ return message;
+ }
+
+ private static String getStringFromCustomTemplate() throws TemplateException {
+ Template template = TemplateEngine.getDefaultImplementation()
+ .newTemplateFromClasspath("templates/customTemplate.ftl");
+ final String message = template.process(new HashMap() {{
+ put("message", "Hello World");
+ }});
+ return message;
+ }
+}
diff --git a/symphony-bdk-examples/bdk-template-examples/src/main/resources/templates/customTemplate.ftl b/symphony-bdk-examples/bdk-template-examples/src/main/resources/templates/customTemplate.ftl
new file mode 100644
index 000000000..8dd7b7358
--- /dev/null
+++ b/symphony-bdk-examples/bdk-template-examples/src/main/resources/templates/customTemplate.ftl
@@ -0,0 +1 @@
+${message} from custom template !
diff --git a/symphony-bdk-examples/pom.xml b/symphony-bdk-examples/pom.xml
index 0cc1544f1..91cac6004 100644
--- a/symphony-bdk-examples/pom.xml
+++ b/symphony-bdk-examples/pom.xml
@@ -17,6 +17,7 @@
bdk-core-examples
bdk-spring-boot-example
+ bdk-template-examples
diff --git a/symphony-bdk-template/pom.xml b/symphony-bdk-template/pom.xml
new file mode 100644
index 000000000..2dea93d4a
--- /dev/null
+++ b/symphony-bdk-template/pom.xml
@@ -0,0 +1,22 @@
+
+
+
+ 4.0.0
+
+ symphony-api-client-java-parent
+ com.symphony.platformsolutions
+ 1.2.0-SNAPSHOT
+
+
+ symphony-bdk-template
+ Symphony Java BDK Template
+ Symphony Java BDK Template Module
+ pom
+
+
+ symphony-bdk-template-api
+ symphony-bdk-template-freemarker
+
+
+
diff --git a/symphony-bdk-template/symphony-bdk-template-api/pom.xml b/symphony-bdk-template/symphony-bdk-template-api/pom.xml
new file mode 100644
index 000000000..611fe1f42
--- /dev/null
+++ b/symphony-bdk-template/symphony-bdk-template-api/pom.xml
@@ -0,0 +1,45 @@
+
+
+
+ symphony-bdk-template
+ com.symphony.platformsolutions
+ 1.2.0-SNAPSHOT
+
+ 4.0.0
+
+ symphony-bdk-template-api
+ Symphony Java BDK Template API
+ Symphony Java BDK Template API module
+
+
+
+
+ com.symphony.platformsolutions
+ symphony-bdk-bom
+ ${project.version}
+ pom
+ import
+
+
+
+
+
+
+ org.apiguardian
+ apiguardian-api
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+
+
+
diff --git a/symphony-bdk-template/symphony-bdk-template-api/src/main/java/com/symphony/bdk/template/api/Template.java b/symphony-bdk-template/symphony-bdk-template-api/src/main/java/com/symphony/bdk/template/api/Template.java
new file mode 100644
index 000000000..80f9f5ec8
--- /dev/null
+++ b/symphony-bdk-template/symphony-bdk-template-api/src/main/java/com/symphony/bdk/template/api/Template.java
@@ -0,0 +1,21 @@
+package com.symphony.bdk.template.api;
+
+import org.apiguardian.api.API;
+
+/**
+ * Interface to represent a template.
+ * A template takes parameters in input and outputs a string.
+ */
+@API(status = API.Status.STABLE)
+public interface Template {
+
+ /**
+ * Produces the string from this template using the given parameters passed in input.
+ * @param parameters the object which contains the parameters to be used by the template.
+ * The actual type depends on the concrete implementation.
+ * Checking the actual type passed in parameter will be the responsibility of each concrete implementation.
+ * @return the generated string where each parameter is replaced by its value provided in the map
+ * @throws TemplateException in case of issues during the string generation, e.g. missing parameter
+ */
+ String process(Object parameters) throws TemplateException;
+}
diff --git a/symphony-bdk-template/symphony-bdk-template-api/src/main/java/com/symphony/bdk/template/api/TemplateEngine.java b/symphony-bdk-template/symphony-bdk-template-api/src/main/java/com/symphony/bdk/template/api/TemplateEngine.java
new file mode 100644
index 000000000..aabeee25b
--- /dev/null
+++ b/symphony-bdk-template/symphony-bdk-template-api/src/main/java/com/symphony/bdk/template/api/TemplateEngine.java
@@ -0,0 +1,72 @@
+package com.symphony.bdk.template.api;
+
+import org.apiguardian.api.API;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+/**
+ * Interface to represent a specific template engine backed by a specific technology.
+ * It creates {@link Template} instances based on a file provided in the file system, in the classpath or as a URL
+ */
+@API(status = API.Status.STABLE)
+public interface TemplateEngine {
+
+ /**
+ * Returns the names of built-in templates which can be retrieved by calling {@link #newBuiltInTemplate(String)}
+ * @return the names of available built-in templates
+ */
+ Set getBuiltInTemplates();
+
+ /**
+ * Creates a new {@link Template} from the built-in template name.
+ * @param template the name of one of the built-in templates returned by {@link #getBuiltInTemplates()}
+ * @return a new {@link Template} instantiated from the provided template name
+ * @throws TemplateException if template not found
+ */
+ Template newBuiltInTemplate(String template) throws TemplateException;
+
+ /**
+ * Create a {@link Template} instance from a file on the file system
+ * @param templatePath path to a template file on the file system
+ * @return a new {@link Template} instantiated from the provided file
+ * @throws TemplateException when template cannot be loaded, e.g. file not accessible
+ */
+ Template newTemplateFromFile(String templatePath) throws TemplateException;
+
+ /**
+ * Create a {@link Template} instance from a file in the classpath
+ * @param templatePath full path to a template file in the classpath
+ * @returna a new {@link Template} instantiated from the provided classpath resource
+ * @throws TemplateException when template cannot be loaded, e.g. resource not accessible
+ */
+ Template newTemplateFromClasspath(String templatePath) throws TemplateException;
+
+ /**
+ * Creates a template from a URL to a template file
+ * @param url the url where to fetch the template file
+ * @return a new {@link Template} instantiated from the provided url, should be a valid {@link java.net.URL} string.
+ * @throws TemplateException when template cannot be loaded, e.g. url not accessible
+ */
+ Template newTemplateFromUrl(String url) throws TemplateException;
+
+ static TemplateEngine getDefaultImplementation() {
+ final ServiceLoader engineServiceLoader = ServiceLoader.load(TemplateEngine.class);
+
+ final List templateEngines = StreamSupport.stream(engineServiceLoader.spliterator(), false)
+ .collect(Collectors.toList());
+
+ if (templateEngines.isEmpty()) {
+ throw new IllegalStateException("No TemplateEngine implementation found in classpath.");
+ } else if (templateEngines.size() > 1) {
+ LoggerFactory.getLogger(TemplateEngine.class)
+ .warn("More than 1 TemplateEngine implementation found in classpath, will use : {}",
+ templateEngines.stream().findFirst().get());
+ }
+ return templateEngines.stream().findFirst().get();
+ }
+}
diff --git a/symphony-bdk-template/symphony-bdk-template-api/src/main/java/com/symphony/bdk/template/api/TemplateException.java b/symphony-bdk-template/symphony-bdk-template-api/src/main/java/com/symphony/bdk/template/api/TemplateException.java
new file mode 100644
index 000000000..d93a37d17
--- /dev/null
+++ b/symphony-bdk-template/symphony-bdk-template-api/src/main/java/com/symphony/bdk/template/api/TemplateException.java
@@ -0,0 +1,17 @@
+package com.symphony.bdk.template.api;
+
+import org.apiguardian.api.API;
+
+
+/**
+ * Exception thrown when instantiating a {@link Template} through {@link TemplateEngine}
+ * or when calling {@link Template#process(Object)}.
+ * This can be triggered when template cannot be loaded,
+ * if template is malformed or if some parameters are missing when calling {@link Template#process(Object)}
+ */
+@API(status = API.Status.STABLE)
+public class TemplateException extends Exception {
+ public TemplateException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/symphony-bdk-template/symphony-bdk-template-freemarker/pom.xml b/symphony-bdk-template/symphony-bdk-template-freemarker/pom.xml
new file mode 100644
index 000000000..b404c3983
--- /dev/null
+++ b/symphony-bdk-template/symphony-bdk-template-freemarker/pom.xml
@@ -0,0 +1,53 @@
+
+
+
+ symphony-bdk-template
+ com.symphony.platformsolutions
+ 1.2.0-SNAPSHOT
+
+ 4.0.0
+
+ symphony-bdk-template-freemarker
+ Symphony Java BDK Template Freemarker
+ Symphony Java BDK Template Freemarker Module
+
+
+
+
+ com.symphony.platformsolutions
+ symphony-bdk-bom
+ ${project.version}
+ pom
+ import
+
+
+
+
+
+
+ org.freemarker
+ freemarker
+ 2.3.30
+
+
+ org.reflections
+ reflections
+ 0.9.12
+
+
+ commons-io
+ commons-io
+
+
+ com.symphony.platformsolutions
+ symphony-bdk-template-api
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+
diff --git a/symphony-bdk-template/symphony-bdk-template-freemarker/src/main/java/com/symphony/bdk/template/freemarker/FreeMarkerEngine.java b/symphony-bdk-template/symphony-bdk-template-freemarker/src/main/java/com/symphony/bdk/template/freemarker/FreeMarkerEngine.java
new file mode 100644
index 000000000..7456d660a
--- /dev/null
+++ b/symphony-bdk-template/symphony-bdk-template-freemarker/src/main/java/com/symphony/bdk/template/freemarker/FreeMarkerEngine.java
@@ -0,0 +1,127 @@
+package com.symphony.bdk.template.freemarker;
+
+import com.symphony.bdk.template.api.Template;
+import com.symphony.bdk.template.api.TemplateEngine;
+import com.symphony.bdk.template.api.TemplateException;
+
+import freemarker.template.Configuration;
+import freemarker.template.TemplateExceptionHandler;
+import org.apache.commons.io.FilenameUtils;
+import org.apiguardian.api.API;
+import org.reflections.Reflections;
+import org.reflections.scanners.ResourcesScanner;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * FreeMarker specific implementation of {@link TemplateEngine}. Instantiates {@link FreeMarkerTemplate} objects.
+ */
+@API(status = API.Status.INTERNAL)
+public class FreeMarkerEngine implements TemplateEngine {
+
+ public static final String FREEMARKER_EXTENSION = ".ftl";
+
+ private static Configuration configuration = createConfiguration();
+ private static Map builtInTemplates = getBuiltInTemplateMap();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set getBuiltInTemplates() {
+ return builtInTemplates.keySet();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Template newBuiltInTemplate(String template) throws TemplateException {
+ if (!builtInTemplates.containsKey(template)) {
+ throw new TemplateException("Template " + template + " not found", null);
+ }
+ return newTemplateFromClasspath(builtInTemplates.get(template));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Template newTemplateFromFile(String templatePath) throws TemplateException {
+ try {
+ return getTemplateFromFile(templatePath);
+ } catch (IOException e) {
+ throw new TemplateException("Unable to open template file", e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Template newTemplateFromClasspath(String templatePath) throws TemplateException {
+ try {
+ configuration.setClassForTemplateLoading(getClass(), "/");
+ return new FreeMarkerTemplate(configuration.getTemplate(templatePath));
+ } catch (IOException e) {
+ throw new TemplateException("Unable to load template from classpath", e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Template newTemplateFromUrl(String url) throws TemplateException {
+ try {
+ return getTemplateFromUrl(url);
+ } catch (IOException e) {
+ throw new TemplateException("Unable to load template from url", e);
+ }
+ }
+
+
+ private FreeMarkerTemplate getTemplateFromFile(String templatePath) throws IOException {
+ String directory = FilenameUtils.getFullPathNoEndSeparator(templatePath);
+ String file = FilenameUtils.getName(templatePath);
+
+ configuration.setDirectoryForTemplateLoading(new File(directory));
+ return new FreeMarkerTemplate(configuration.getTemplate(file));
+ }
+
+ private Template getTemplateFromUrl(String url) throws IOException {
+ String baseUrl = FilenameUtils.getFullPathNoEndSeparator(url);
+ String file = FilenameUtils.getName(url);
+
+ configuration.setTemplateLoader(new UrlTemplateLoader(baseUrl));
+ return new FreeMarkerTemplate(configuration.getTemplate(file));
+ }
+
+ private static Configuration createConfiguration() {
+ Configuration cfg = new Configuration(Configuration.VERSION_2_3_29);
+ cfg.setDefaultEncoding("UTF-8");
+ cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
+ cfg.setLogTemplateExceptions(false);
+ cfg.setWrapUncheckedExceptions(true);
+ cfg.setFallbackOnNullLoopVariable(false);
+ return cfg;
+ }
+
+ private static Map getBuiltInTemplateMap() {
+ Reflections reflections = new Reflections(FreeMarkerEngine.class.getPackage().getName(), new ResourcesScanner());
+
+ final Set resources = reflections.getResources(n -> n.endsWith(FREEMARKER_EXTENSION));
+ return resources.stream().collect(Collectors.toMap(r -> extractTemplateName(r), Function.identity()));
+ }
+
+ private static String extractTemplateName(String templatePath) {
+ // remove the path part until the last '/' and the FREEMARKER_EXTENSION
+ return templatePath.substring(templatePath.lastIndexOf('/') + 1,
+ templatePath.length() - FREEMARKER_EXTENSION.length());
+ }
+}
diff --git a/symphony-bdk-template/symphony-bdk-template-freemarker/src/main/java/com/symphony/bdk/template/freemarker/FreeMarkerTemplate.java b/symphony-bdk-template/symphony-bdk-template-freemarker/src/main/java/com/symphony/bdk/template/freemarker/FreeMarkerTemplate.java
new file mode 100644
index 000000000..0d872d095
--- /dev/null
+++ b/symphony-bdk-template/symphony-bdk-template-freemarker/src/main/java/com/symphony/bdk/template/freemarker/FreeMarkerTemplate.java
@@ -0,0 +1,44 @@
+package com.symphony.bdk.template.freemarker;
+
+
+import com.symphony.bdk.template.api.Template;
+import com.symphony.bdk.template.api.TemplateException;
+
+import org.apiguardian.api.API;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Map;
+
+/**
+ * FreeMarker specific implementation of {@link Template}
+ */
+@API(status = API.Status.INTERNAL)
+public class FreeMarkerTemplate implements Template {
+
+ private final freemarker.template.Template template;
+
+ public FreeMarkerTemplate(freemarker.template.Template template) {
+ this.template = template;
+ }
+
+ /**
+ * Produces the string from this template using the given parameters passed in input.
+ * @param parameters the object which contains the parameters to be used by the template.
+ * Checlk FreeMarker documentation to know more about accepted objects:
+ * @see Create the data model
+ * @return the generated string where each parameter is replaced by its value provided in the map
+ * @throws TemplateException in case of issues during the string generation, e.g. missing parameter
+ */
+ @Override
+ public String process(Object parameters) throws TemplateException {
+ try {
+ Writer out = new StringWriter();
+ template.process(parameters, out);
+ return out.toString();
+ } catch (freemarker.template.TemplateException | IOException e) {
+ throw new TemplateException("Could not generate string from template", e);
+ }
+ }
+}
diff --git a/symphony-bdk-template/symphony-bdk-template-freemarker/src/main/java/com/symphony/bdk/template/freemarker/UrlTemplateLoader.java b/symphony-bdk-template/symphony-bdk-template-freemarker/src/main/java/com/symphony/bdk/template/freemarker/UrlTemplateLoader.java
new file mode 100644
index 000000000..9db8bb2bb
--- /dev/null
+++ b/symphony-bdk-template/symphony-bdk-template-freemarker/src/main/java/com/symphony/bdk/template/freemarker/UrlTemplateLoader.java
@@ -0,0 +1,40 @@
+package com.symphony.bdk.template.freemarker;
+
+import freemarker.cache.URLTemplateLoader;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Internal class used when instantiating a {@link FreeMarkerTemplate} with {@link FreeMarkerEngine#newTemplateFromUrl(String)}
+ */
+class UrlTemplateLoader extends URLTemplateLoader {
+
+ private String baseUrl;
+
+ public UrlTemplateLoader(String baseUrl) {
+ this.baseUrl = baseUrl;
+ }
+
+ @Override
+ protected URL getURL(String s) {
+ try {
+ URL url = new URL(baseUrl + "/" + s);
+ if (resourceCanBeAccessed(url)) {
+ return url;
+ }
+ } catch (MalformedURLException e) {
+ }
+ return null;
+ }
+
+ private boolean resourceCanBeAccessed(URL url) {
+ try (InputStream ignored = url.openStream()) {
+ } catch (IOException e) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/symphony-bdk-template/symphony-bdk-template-freemarker/src/main/resources/META-INF/services/com.symphony.bdk.template.api.TemplateEngine b/symphony-bdk-template/symphony-bdk-template-freemarker/src/main/resources/META-INF/services/com.symphony.bdk.template.api.TemplateEngine
new file mode 100644
index 000000000..f49001433
--- /dev/null
+++ b/symphony-bdk-template/symphony-bdk-template-freemarker/src/main/resources/META-INF/services/com.symphony.bdk.template.api.TemplateEngine
@@ -0,0 +1 @@
+com.symphony.bdk.template.freemarker.FreeMarkerEngine
diff --git a/symphony-bdk-template/symphony-bdk-template-freemarker/src/main/resources/com/symphony/bdk/template/freemarker/simpleMML.ftl b/symphony-bdk-template/symphony-bdk-template-freemarker/src/main/resources/com/symphony/bdk/template/freemarker/simpleMML.ftl
new file mode 100644
index 000000000..8dd569228
--- /dev/null
+++ b/symphony-bdk-template/symphony-bdk-template-freemarker/src/main/resources/com/symphony/bdk/template/freemarker/simpleMML.ftl
@@ -0,0 +1 @@
+${message}
diff --git a/symphony-bdk-template/symphony-bdk-template-freemarker/src/test/java/com/symphony/bdk/template/freemarker/FreeMarkerTemplateTest.java b/symphony-bdk-template/symphony-bdk-template-freemarker/src/test/java/com/symphony/bdk/template/freemarker/FreeMarkerTemplateTest.java
new file mode 100644
index 000000000..b58e3e152
--- /dev/null
+++ b/symphony-bdk-template/symphony-bdk-template-freemarker/src/test/java/com/symphony/bdk/template/freemarker/FreeMarkerTemplateTest.java
@@ -0,0 +1,128 @@
+package com.symphony.bdk.template.freemarker;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import com.symphony.bdk.template.api.Template;
+import com.symphony.bdk.template.api.TemplateEngine;
+import com.symphony.bdk.template.api.TemplateException;
+
+import org.junit.jupiter.api.Test;
+
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Test class for {@link FreeMarkerEngine} and {@link FreeMarkerTemplate}
+ */
+public class FreeMarkerTemplateTest {
+
+ @Test
+ public void testDefaultImplementation() {
+ TemplateEngine defaultImplementation = TemplateEngine.getDefaultImplementation();
+ assertEquals(FreeMarkerEngine.class, defaultImplementation.getClass());
+ }
+
+ @Test
+ public void testFromFile() throws TemplateException {
+ Template freeMarkerTemplate = new FreeMarkerEngine().newTemplateFromFile("./src/test/resources/subFolder/test.ftl");
+ assertTemplateProducesOutput(freeMarkerTemplate);
+ }
+
+ @Test
+ public void testFromFileWithInclude() throws TemplateException {
+ Template freeMarkerTemplate = new FreeMarkerEngine()
+ .newTemplateFromFile("./src/test/resources/subFolder/testWithInclude.ftl");
+ assertTemplateProducesOutput(freeMarkerTemplate, new HashMap<>(),
+ "Template with include\nHello from included file!\n");
+ }
+
+ @Test
+ public void testFromNotFoundFile() {
+ assertThrows(TemplateException.class, () -> new FreeMarkerEngine().newTemplateFromFile("./not/found.ftl"));
+ }
+
+ @Test
+ public void testFromClasspath() throws TemplateException {
+ Template freeMarkerTemplate = new FreeMarkerEngine().newTemplateFromClasspath("/subFolder/test.ftl");
+ assertTemplateProducesOutput(freeMarkerTemplate);
+ }
+
+ @Test
+ public void testFromClasspathRelativePath() throws TemplateException {
+ Template freeMarkerTemplate = new FreeMarkerEngine().newTemplateFromClasspath("subFolder/test.ftl");
+ assertTemplateProducesOutput(freeMarkerTemplate);
+ }
+
+ @Test
+ public void testFromClasspathRelativePathNoSlash() throws TemplateException {
+ Template freeMarkerTemplate = new FreeMarkerEngine().newTemplateFromClasspath("subFolder/test.ftl");
+ assertTemplateProducesOutput(freeMarkerTemplate);
+ }
+
+ @Test
+ public void testFromClasspatWithInclude() throws TemplateException {
+ Template freeMarkerTemplate = new FreeMarkerEngine().newTemplateFromClasspath("/subFolder/testWithInclude.ftl");
+ assertTemplateProducesOutput(freeMarkerTemplate, new HashMap<>(),
+ "Template with include\nHello from included file!\n");
+ }
+
+ @Test
+ public void testWithNotFoundResource() {
+ assertThrows(TemplateException.class, () -> new FreeMarkerEngine().newTemplateFromClasspath("./not/found.ftl"));
+ }
+
+ @Test
+ public void testFromUrl() throws TemplateException {
+ URL baseUrl = getClass().getResource("/subFolder/test.ftl");
+
+ Template freeMarkerTemplate = new FreeMarkerEngine().newTemplateFromUrl(baseUrl.toString());
+ assertTemplateProducesOutput(freeMarkerTemplate);
+ }
+
+ @Test
+ public void testFromUrlWithInclude() throws TemplateException {
+ URL baseUrl = getClass().getResource("/subFolder/testWithInclude.ftl");
+
+ Template freeMarkerTemplate = new FreeMarkerEngine().newTemplateFromUrl(baseUrl.toString());
+ assertTemplateProducesOutput(freeMarkerTemplate, new HashMap<>(),
+ "Template with include\nHello from included file!\n");
+ }
+
+ @Test
+ public void testFromUrlWithNotFoundResource() {
+ assertThrows(TemplateException.class, () -> new FreeMarkerEngine().newTemplateFromUrl("file:/not/found/file.ftl"));
+ }
+
+ private void assertTemplateProducesOutput(Template freeMarkerTemplate) throws TemplateException {
+ Map parameters = new HashMap<>();
+ parameters.put("message", "Hello World!");
+
+ assertTemplateProducesOutput(freeMarkerTemplate, parameters, "Hello World!\n");
+ }
+
+ @Test
+ public void testgetBuiltInTemplates() {
+ assertEquals(Collections.singleton("simpleMML"), new FreeMarkerEngine().getBuiltInTemplates());
+ }
+
+ @Test
+ public void testFromBuiltInTemplate() throws TemplateException {
+ final Template simpleMML = new FreeMarkerEngine().newBuiltInTemplate("simpleMML");
+ assertTemplateProducesOutput(simpleMML);
+ }
+
+ @Test
+ public void testFromNonExistingBuiltInTemplate() {
+ assertThrows(TemplateException.class, () -> new FreeMarkerEngine().newBuiltInTemplate("notFound"));
+ }
+
+ private void assertTemplateProducesOutput(Template freeMarkerTemplate, Object parameters, String expectedOutput)
+ throws TemplateException {
+ assertEquals(FreeMarkerTemplate.class, freeMarkerTemplate.getClass());
+ assertEquals(expectedOutput, freeMarkerTemplate.process(parameters));
+ }
+
+}
diff --git a/symphony-bdk-template/symphony-bdk-template-freemarker/src/test/resources/subFolder/included.ftl b/symphony-bdk-template/symphony-bdk-template-freemarker/src/test/resources/subFolder/included.ftl
new file mode 100644
index 000000000..ddf249336
--- /dev/null
+++ b/symphony-bdk-template/symphony-bdk-template-freemarker/src/test/resources/subFolder/included.ftl
@@ -0,0 +1 @@
+Hello from included file!
diff --git a/symphony-bdk-template/symphony-bdk-template-freemarker/src/test/resources/subFolder/test.ftl b/symphony-bdk-template/symphony-bdk-template-freemarker/src/test/resources/subFolder/test.ftl
new file mode 100644
index 000000000..8dd569228
--- /dev/null
+++ b/symphony-bdk-template/symphony-bdk-template-freemarker/src/test/resources/subFolder/test.ftl
@@ -0,0 +1 @@
+${message}
diff --git a/symphony-bdk-template/symphony-bdk-template-freemarker/src/test/resources/subFolder/testWithInclude.ftl b/symphony-bdk-template/symphony-bdk-template-freemarker/src/test/resources/subFolder/testWithInclude.ftl
new file mode 100644
index 000000000..d49285f6c
--- /dev/null
+++ b/symphony-bdk-template/symphony-bdk-template-freemarker/src/test/resources/subFolder/testWithInclude.ftl
@@ -0,0 +1,2 @@
+Template with include
+<#include "included.ftl">