diff --git a/jersey/pom.xml b/jersey/pom.xml
index 78e6d621ad..e248f9cf90 100644
--- a/jersey/pom.xml
+++ b/jersey/pom.xml
@@ -29,6 +29,28 @@
jaxrs-ri
${jersey.version}
+
+ org.glassfish.jersey.containers
+ jersey-container-grizzly2-servlet
+ ${jersey.version}
+
+
+ org.glassfish.jersey.ext
+ jersey-mvc-freemarker
+ ${jersey.version}
+
+
+ org.glassfish.jersey.test-framework
+ jersey-test-framework-core
+ ${jersey.version}
+ test
+
+
+ org.glassfish.jersey.test-framework.providers
+ jersey-test-framework-provider-grizzly2
+ ${jersey.version}
+ test
+
@@ -41,6 +63,13 @@
false
+
+ org.codehaus.mojo
+ exec-maven-plugin
+
+ com.baeldung.jersey.server.http.EmbeddedHttpServer
+
+
diff --git a/jersey/src/main/java/com/baeldung/jersey/server/config/ViewApplicationConfig.java b/jersey/src/main/java/com/baeldung/jersey/server/config/ViewApplicationConfig.java
new file mode 100644
index 0000000000..d4744066c4
--- /dev/null
+++ b/jersey/src/main/java/com/baeldung/jersey/server/config/ViewApplicationConfig.java
@@ -0,0 +1,14 @@
+package com.baeldung.jersey.server.config;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.freemarker.FreemarkerMvcFeature;
+
+public class ViewApplicationConfig extends ResourceConfig {
+
+ public ViewApplicationConfig() {
+ packages("com.baeldung.jersey.server");
+ property(FreemarkerMvcFeature.TEMPLATE_BASE_PATH, "templates/freemarker");
+ register(FreemarkerMvcFeature.class);;
+ }
+
+}
diff --git a/jersey/src/main/java/com/baeldung/jersey/server/http/EmbeddedHttpServer.java b/jersey/src/main/java/com/baeldung/jersey/server/http/EmbeddedHttpServer.java
new file mode 100644
index 0000000000..4afa086858
--- /dev/null
+++ b/jersey/src/main/java/com/baeldung/jersey/server/http/EmbeddedHttpServer.java
@@ -0,0 +1,35 @@
+package com.baeldung.jersey.server.http;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+
+import com.baeldung.jersey.server.config.ViewApplicationConfig;
+
+public class EmbeddedHttpServer {
+
+ private static final URI BASE_URI = URI.create("http://localhost:8082/");
+
+ public static void main(String[] args) {
+ try {
+ final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, new ViewApplicationConfig(), false);
+
+ Runtime.getRuntime()
+ .addShutdownHook(new Thread(() -> {
+ server.shutdownNow();
+ }));
+
+ server.start();
+
+ System.out.println(String.format("Application started.\nTry out %s\nStop the application using CTRL+C", BASE_URI + "fruit"));
+ } catch (IOException ex) {
+ Logger.getLogger(EmbeddedHttpServer.class.getName())
+ .log(Level.SEVERE, null, ex);
+ }
+
+ }
+}
diff --git a/jersey/src/main/java/com/baeldung/jersey/server/model/Fruit.java b/jersey/src/main/java/com/baeldung/jersey/server/model/Fruit.java
new file mode 100644
index 0000000000..da4865e9ab
--- /dev/null
+++ b/jersey/src/main/java/com/baeldung/jersey/server/model/Fruit.java
@@ -0,0 +1,23 @@
+package com.baeldung.jersey.server.model;
+
+public class Fruit {
+
+ private final String name;
+ private final String colour;
+
+ public Fruit(String name, String colour) {
+ this.name = name;
+ this.colour = colour;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getColour() {
+ return colour;
+ }
+
+
+
+}
diff --git a/jersey/src/main/java/com/baeldung/jersey/server/rest/FruitResource.java b/jersey/src/main/java/com/baeldung/jersey/server/rest/FruitResource.java
new file mode 100644
index 0000000000..4e1fa4aa11
--- /dev/null
+++ b/jersey/src/main/java/com/baeldung/jersey/server/rest/FruitResource.java
@@ -0,0 +1,55 @@
+package com.baeldung.jersey.server.rest;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.server.mvc.ErrorTemplate;
+import org.glassfish.jersey.server.mvc.Template;
+import org.glassfish.jersey.server.mvc.Viewable;
+
+import com.baeldung.jersey.server.model.Fruit;
+
+@Path("/fruit")
+public class FruitResource {
+
+ @GET
+ public Viewable get() {
+ return new Viewable("/index.ftl", "Fruit Index Page");
+ }
+
+ @GET
+ @Template(name = "/all.ftl")
+ @Path("/all")
+ @Produces(MediaType.TEXT_HTML)
+ public Map getAllFruit() {
+ final List fruits = new ArrayList<>();
+ fruits.add(new Fruit("banana", "yellow"));
+ fruits.add(new Fruit("apple", "red"));
+ fruits.add(new Fruit("kiwi", "green"));
+
+ final Map model = new HashMap();
+ model.put("items", fruits);
+ return model;
+ }
+
+ @GET
+ @ErrorTemplate(name = "/error.ftl")
+ @Template(name = "/named.ftl")
+ @Path("{name}")
+ @Produces(MediaType.TEXT_HTML)
+ public String getFruitByName(@PathParam("name") String name) {
+ if (!"banana".equalsIgnoreCase(name)) {
+ throw new IllegalArgumentException("Fruit not found: " + name);
+ }
+ return name;
+ }
+
+}
diff --git a/jersey/src/main/resources/templates/freemarker/all.ftl b/jersey/src/main/resources/templates/freemarker/all.ftl
new file mode 100644
index 0000000000..59406a60ca
--- /dev/null
+++ b/jersey/src/main/resources/templates/freemarker/all.ftl
@@ -0,0 +1,14 @@
+
+
+ All fruit!
+
+
+ All fruit!
+ Fruits:
+
+ <#list items as fruit>
+ - ${fruit.name}
+ #list>
+
+
+
\ No newline at end of file
diff --git a/jersey/src/main/resources/templates/freemarker/error.ftl b/jersey/src/main/resources/templates/freemarker/error.ftl
new file mode 100644
index 0000000000..8ea6828ba5
--- /dev/null
+++ b/jersey/src/main/resources/templates/freemarker/error.ftl
@@ -0,0 +1,8 @@
+
+
+ Welcome!
+
+
+ Error - ${model.message}!
+
+
\ No newline at end of file
diff --git a/jersey/src/main/resources/templates/freemarker/index.ftl b/jersey/src/main/resources/templates/freemarker/index.ftl
new file mode 100644
index 0000000000..01447f24e8
--- /dev/null
+++ b/jersey/src/main/resources/templates/freemarker/index.ftl
@@ -0,0 +1,8 @@
+
+
+ Welcome!
+
+
+ Welcome ${model}!
+
+
\ No newline at end of file
diff --git a/jersey/src/main/resources/templates/freemarker/named.ftl b/jersey/src/main/resources/templates/freemarker/named.ftl
new file mode 100644
index 0000000000..ccde3307e6
--- /dev/null
+++ b/jersey/src/main/resources/templates/freemarker/named.ftl
@@ -0,0 +1,8 @@
+
+
+ Welcome!
+
+
+ Found fruit - ${model}!
+
+
\ No newline at end of file
diff --git a/jersey/src/test/java/com/baeldung/jersey/server/rest/FruitResourceIntegrationTest.java b/jersey/src/test/java/com/baeldung/jersey/server/rest/FruitResourceIntegrationTest.java
new file mode 100644
index 0000000000..a0b6daed51
--- /dev/null
+++ b/jersey/src/test/java/com/baeldung/jersey/server/rest/FruitResourceIntegrationTest.java
@@ -0,0 +1,42 @@
+package com.baeldung.jersey.server.rest;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.baeldung.jersey.server.config.ViewApplicationConfig;
+
+public class FruitResourceIntegrationTest extends JerseyTest {
+
+ @Override
+ protected Application configure() {
+ return new ViewApplicationConfig();
+ }
+
+ @Test
+ public void testAllFruit() {
+ final String response = target("/fruit/all").request()
+ .get(String.class);
+ Assert.assertThat(response, allOf(containsString("banana"), containsString("apple"), containsString("kiwi")));
+ }
+
+ @Test
+ public void testIndex() {
+ final String response = target("/fruit").request()
+ .get(String.class);
+ Assert.assertThat(response, containsString("Welcome Fruit Index Page!"));
+ }
+
+ @Test
+ public void testErrorTemplate() {
+ final String response = target("/fruit/orange").request()
+ .get(String.class);
+ Assert.assertThat(response, containsString("Error - Fruit not found: orange!"));
+ }
+
+}