Table of Contents
Jersey provides an extension to support the Model-View-Controller (MVC) design pattern. In the context of Jersey components, the Controller from the MVC pattern corresponds to a resource class or method, the View to a template bound to the resource class or method, and the model to a Java object (or a Java bean) returned from a resource method (Controller).
Some of the passages/examples from this chapter have been created by Paul Sandoz.
In Jersey 2, the base MVC API consists of two classes (org.glassfish.jersey.server.mvc
package)
that can be used to bind model to view (template), namely Viewable and @Template.
These classes determine which approach (explicit/implicit) you would be taking when working with Jersey MVC
templating support.
In this approach a resource method explicitly returns a reference to a view template and the data model to be
used. For this purpose the Viewable class has been introduced in Jersey 1 and is also
present (under a different package) in Jersey 2 and 3. A simple example of usage can be seen in
Example 22.1, “Using Viewable
in a resource class”.
Example 22.1. Using Viewable
in a resource class
package com.example; @Path("foo") public class Foo { @GET public Viewable get() { return new Viewable("index.foo", "FOO"); } }
In this example, the Foo
JAX-RS resource class is the controller and the
Viewable
instance encapsulates the provided data model (FOO
string)
and a named reference to the associated view template (index.foo
).
All HTTP methods may return Viewable
instances. Thus a POST
method may
return a template reference to a template that produces a view as a result of processing an
HTML Form.
There is no need to use Viewable
every time you want to bind a model to a template. To
make the resource method more readable (and to avoid verbose wrapping of a template reference and model into
Viewable
) you can simply annotate a resource method with @Template
annotation. An updated example, using @Template
, from previous section is shown in
Example 22.2, “Using @Template
on a resource method” example.
Example 22.2. Using @Template
on a resource method
package com.example; @Path("foo") public class Foo { @GET @Template(name = "index.foo") public String get() { return "FOO"; } }
In this example, the Foo
JAX-RS resource class is still the controller as in previous
section but the MVC model is now represented by the return value of annotated resource method.
The processing of such a method is then essentially the same as if the return type of the method was an
instance of the Viewable class. If a method is annotated with
@Template
and is also returning a
Viewable
instance then the values from the
Viewable
instance take precedence over those defined in the annotation. Producible
media types are for both cases, Viewable
and @Template
,
determined by the method or class level @Produces
annotation.
A resource class can have templates implicitly associated with it via @Template annotation.
For example, take a look at the resource class listing in Example 22.3, “Using @Template
on a resource class”.
Example 22.3. Using @Template
on a resource class
@Path("foo") @Template public class Foo { public String getFoo() { return "FOO"; } }
The example relies on Jersey MVC conventions a lot and requires more explanation as such. First of all, you may
have noticed that there is no resource method defined in this JAX-RS resource. Also, there is no template
reference defined.
In this case, since the @Template
annotation placed on the resource class does not
contain any information, the default relative template reference index
will be used (for more
on this topic see Section 22.3, “Absolute vs. Relative template reference”).
As for the missing resource methods, a default @GET
method will be automatically generated by Jersey
for the Foo
resource (which is the MVC Controller now). The implementation of the generated
resource method performs the equivalent of the following explicit resource method:
@GET public Viewable get() { return new Viewable("index", this); }
You can see that the resource class serves in this case also as the model. Producible media types are determined
based on the @Produces
annotation declared on the resource class, if any.
In case of "resource class"-based implicit MVC view templates, the controller is also the model. In such
case the template reference index
is special, it is the template reference
associated with the controller instance itself.
In the following example, the MVC controller represented by a JAX-RS @GET
sub-resource method,
is also generated in the resource class annotated with @Template
:
@GET @Path("{implicit-view-path-parameter}") public Viewable get(@PathParameter("{implicit-view-path-parameter}") String template) { return new Viewable(template, this); }
This allows Jersey to support also implicit sub-resource templates. For example, a JAX-RS resource at path
foo/bar
will try to use relative template reference bar
that resolves to an
absolute template reference /com/foo/Foo/bar
.
In other words, a HTTP GET
request to a /foo/bar
would be handled by this
auto-generated method in the Foo
resource and would delegate the request to a registered
template processor supports processing of the absolute template reference
/com/foo/Foo/bar
, where the model is still an instance of the same JAX-RS resource class
Foo
.
As discussed in the previous section, both @Template and Viewable provide means to define a reference to a template. We will now discuss how these values are interpreted and how the concrete template is found.
Relative reference is any path that does not start with a leading '/
' (slash) character (i.e.
index.foo
). This kind of references is resolved into absolute ones by pre-pending a given value
with a fully qualified name of the last matched resource.
Consider the Example 22.3, “Using @Template
on a resource class” from the previous section,
the template name reference index
is a relative value that Jersey will resolve to its
absolute template reference using a fully qualified class name of Foo
(more on resolving
relative template name to the absolute one can be found in the JavaDoc of Viewable class),
which, in our case, is:
"/com/foo/Foo/index"
Jersey will then search all the registered template processors (see Section 22.7, “Writing Custom Templating Engines”) to find a template processor that can resolve the absolute template reference further to a "processable" template reference. If a template processor is found then the "processable" template is processed using the supplied data model.
If none or empty template reference is provided (either in Viewable
or via
@Template
) then the index
reference is assumed and all further
processing is done for this value.
Let's change the resource GET
method in our Foo
resource a little:
Example 22.4. Using absolute path to template in Viewable
@GET public Viewable get() { return new Viewable("/index", "FOO"); }
In this case, since the template reference begins with "/"
, Jersey will consider the reference
to be absolute already and will not attempt to absolutize it again. The reference will be used "as is" when
resolving it to a "processable" template reference as described earlier.
Absolute template references start with leading '/
' (i.e. /com/example/index.foo
)
character and are not further resolved (with respect to the resolving resource class) which means that the
template is looked for at the provided path directly.
Note, however, that template processors for custom templating engines may modify (and the supported ones do)
absolute template reference by pre-pending 'base template path' (if defined) and appending template suffix (i.e.
foo
) if the suffix is not provided in the reference.
For example assume that we want to use Mustache templates for our views and we have defined 'base template path'
as pages
. For the absolute template reference /com/example/Foo/index
the template
processor will transform the reference into the following path: /pages/com/example/Foo/index.mustache
.
In addition to @Template an @ErrorTemplate annotation has been introduced in Jersey 2.3 (valid for Jersey 3.x as well). The purpose of this annotation is to bind the model to an error view in case an exception has been raised during processing of a request. This is true for any exception thrown after the resource matching phase (i.e. this not only applies to JAX-RS resources but providers and even Jersey runtime as well). The model in this case is the thrown exception itself.
Example 22.5, “Using @ErrorTemplate
on a resource method” shows how to use @ErrorTemplate
on a resource method.
If all goes well with the method processing, then the /short-link
template is used as page sent
to the user. Otherwise if an exception is raised then the /error-form
template is shown to the user.
Example 22.5. Using @ErrorTemplate
on a resource method
@POST @Produces({"text/html”}) @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Template(name = "/short-link") @ErrorTemplate(name = "/error-form") public ShortenedLink createLink(@FormParam("link") final String link) { // ... }
Note that @ErrorTemplate can be used on a resource class or a resource method to merely handle error states. There is no need to use @Template or Viewable with it.
The annotation is handled by custom ExceptionMapper<E extends Throwable> which creates an instance of
Viewable
that is further processed by Jersey. This exception mapper is registered automatically
with a MvcFeature
.
@ErrorTemplate
can also be used with Bean Validation to display specific error pages in
case the validation of input/output values fails for some reason. Everything works as described above except the
model is not the thrown exception but rather a list of ValidationErrors. This list can be iterated in
the template and all the validation errors can be shown to the user in a desirable way.
Example 22.6. Using @ErrorTemplate
with Bean Validation
@POST @Produces({"text/html”}) @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Template(name = "/short-link”) @ErrorTemplate(name = "/error-form") @Valid public ShortenedLink createLink(@NotEmpty @FormParam("link") final String link) { // ... }
Example 22.7. Iterating through ValidationError
in JSP
<c:forEach items="${model}" var="error"> ${error.message} "<strong>${error.invalidValue}</strong>"<br/> </c:forEach>
Support for Bean Validation in Jersey MVC Templates is provided by a jersey-mvc-bean-validation
extension module. The JAX-RS Feature provided by this module
(MvcBeanValidationFeature
) has to be registered in order to use this
functionality (see Section 22.5, “Registration and Configuration”).
Maven users can find this module at coordinates
<dependency> <groupId>org.glassfish.jersey.ext</groupId> <artifactId>jersey-mvc-bean-validation</artifactId> <version>3.1.9</version> </dependency>
and for non-Maven users the list of dependencies is available at jersey-mvc-bean-validation.
To use the capabilities of Jersey MVC templating support in your JAX-RS/Jersey application you need to register
specific JAX-RS Features provided by the MVC modules. For jersey-mvc
module it is
MvcFeature for others it could be, for example, FreemarkerMvcFeature
(jersey-mvc-freemarker
).
Example 22.8. Registering MvcFeature
new ResourceConfig() .register(org.glassfish.jersey.server.mvc.MvcFeature.class) // Further configuration of ResourceConfig. .register( ... );
Example 22.9. Registering FreemarkerMvcFeature
new ResourceConfig() .register(org.glassfish.jersey.server.mvc.freemarker.FreemarkerMvcFeature.class) // Further configuration of ResourceConfig. .register( ... );
Modules that uses capabilities of the base Jersey MVC module register MvcFeature
automatically, so you don't need to register this feature explicitly in your code.
Almost all of the MVC modules are further configurable and either contain a *Properties
(e.g. FreemarkerMvcProperties
) class describing all the available properties which could be
set in a JAX-RS Application
/ ResourceConfig
. Alternatively, the properties
are listed directly in the module *Feature
class.
Example 22.10. Setting MvcFeature.TEMPLATE_BASE_PATH
value in ResourceConfig
new ResourceConfig() .property(MvcFeature.TEMPLATE_BASE_PATH, "templates") .register(MvcFeature.class) // Further configuration of ResourceConfig. .register( ... );
Example 22.11. Setting FreemarkerMvcProperties.TEMPLATE_BASE_PATH
value in web.xml
<servlet> <servlet-name>org.glassfish.jersey.examples.freemarker.MyApplication</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> <init-param> <param-name>jakarta.ws.rs.Application</param-name> <param-value>org.glassfish.jersey.examples.freemarker.MyApplication</param-value> </init-param> <init-param> <param-name>jersey.config.server.mvc.templateBasePath.freemarker</param-name> <param-value>freemarker</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
Jersey provides extension modules that enable support for several templating engines. This section lists all the supported engines and their modules as well as discusses any module-specific details.
An integration module for Mustache-based templating engine.
Mustache template processor resolves absolute template references to processable template references represented as Mustache templates as follows:
Procedure 22.1. Resolving Mustache template reference
if the absolute template reference does not end in .mustache
append this suffix to the
reference; and
if ServletContext.getResource
, Class.getResource
or
File.exists
returns a non-null
value for the reference then
return the reference as the processable template reference otherwise return null
(to indicate the absolute reference has not been resolved by the Mustache template processor).
Thus the absolute template reference /com/foo/Foo/index
would be resolved as
/com/foo/Foo/index.mustache
, provided there exists a
/com/foo/Foo/index.mustache
Mustache template in the application.
Available configuration properties:
MustacheMvcFeature.TEMPLATE_BASE_PATH
-
jersey.config.server.mvc.templateBasePath.mustache
The base path where Mustache templates are located.
MustacheMvcFeature.CACHE_TEMPLATES
-
jersey.config.server.mvc.caching.mustache
Enables caching of Mustache templates to avoid multiple compilation.
MustacheMvcFeature.TEMPLATE_OBJECT_FACTORY
-
jersey.config.server.mvc.factory.mustache
Property used to pass user-configured MustacheFactory
.
MustacheMvcFeature.ENCODING
-
jersey.config.server.mvc.encoding.mustache
Property used to configure a default encoding that will be used if none is specified in @Produces annotation. If property is not defined the UTF-8 encoding will be used as a default value.
Maven users can find this module at coordinates
<dependency> <groupId>org.glassfish.jersey.ext</groupId> <artifactId>jersey-mvc-mustache</artifactId> <version>3.1.9</version> </dependency>
and for non-Maven users the list of dependencies is available at jersey-mvc-mustache.
An integration module for Freemarker-based templating engine.
Freemarker template processor resolves absolute template references to processable template references represented as Freemarker templates as follows:
Procedure 22.2. Resolving Freemarker template reference
if the absolute template reference does not end in .ftl
append this suffix to the
reference; and
if ServletContext.getResource
, Class.getResource
or
File.exists
returns a non-null
value for the reference then
return the reference as the processable template reference otherwise return null
(to indicate the absolute reference has not been resolved by the Freemarker template processor).
Thus the absolute template reference /com/foo/Foo/index
would be resolved to
/com/foo/Foo/index.ftl
, provided there exists a /com/foo/Foo/index.ftl
Freemarker template in the application.
Jersey will assign the model instance to an attribute named model
.
So it is possible to reference the foo
key from the provided Map
(MVC Model)
resource from the Freemarker template as follows:
<h1>${model.foo}</h1>
Available configuration properties:
FreemarkerMvcFeature.TEMPLATE_BASE_PATH
-
jersey.config.server.mvc.templateBasePath.freemarker
The base path where Freemarker templates are located.
FreemarkerMvcFeature.CACHE_TEMPLATES
-
jersey.config.server.mvc.caching.freemarker
Enables caching of Freemarker templates to avoid multiple compilation.
FreemarkerMvcFeature.TEMPLATE_OBJECT_FACTORY
-
jersey.config.server.mvc.factory.freemarker
Property used to pass user-configured FreemarkerFactory
.
FreemarkerMvcFeature.ENCODING
-
jersey.config.server.mvc.encoding.freemarker
Property used to configure a default encoding that will be used if none is specified in @Produces annotation. If property is not defined the UTF-8 encoding will be used as a default value.
Maven users can find this module at coordinates
<dependency> <groupId>org.glassfish.jersey.ext</groupId> <artifactId>jersey-mvc-freemarker</artifactId> <version>3.1.9</version> </dependency>
and for non-Maven users the list of dependencies is available at jersey-mvc-freemarker.
An integration module for JSP-based templating engine.
Jersey web applications that want to use JSP templating support should be registered as Servlet
filters rather than Servlets in the application's web.xml
. The
web.xml
-less deployment style introduced in Servlet 3.0 is not supported at the moment
for web applications that require use of Jersey MVC templating support.
JSP template processor resolves absolute template references to processable template references represented as JSP pages as follows:
Procedure 22.3. Resolving JSP template reference
if the absolute template reference does not end in .jsp
append this suffix to the
reference; and
if ServletContext.getResource
returns a non-null
value for the reference then
return the reference as the processable template reference otherwise return null
(to indicate the absolute reference has not been resolved by the JSP template processor).
Thus the absolute template reference /com/foo/Foo/index
would be resolved to
/com/foo/Foo/index.jsp
, provided there exists a /com/foo/Foo/index.jsp
JSP page in the web application.
Jersey will assign the model instance to the attribute named model
or it
.
So it is possible to reference the foo
property on the Foo
resource
from the JSP template as follows:
<h1>${model.foo}</h1>
or
<h1>${it.foo}</h1>
To include another JSP page in the currently processed one a custom include
tag can be used.
Mandatory parameter page
represents a relative template name which would be absolutized using
the same resolving resource class as the parent JSP page template.
Example 22.12. Including JSP page into JSP page
<%@page contentType="text/html"%> <%@page pageEncoding="UTF-8"%> <%@taglib prefix="rbt" uri="urn:org:glassfish:jersey:servlet:mvc" %> <html> <body> <rbt:include page="include.jsp"/> </body> </html>
Available configuration properties:
JspMvcFeature.TEMPLATE_BASE_PATH
-
jersey.config.server.mvc.templateBasePath.jsp
The base path where JSP templates are located.
Maven users can find this module at coordinates
<dependency> <groupId>org.glassfish.jersey.ext</groupId> <artifactId>jersey-mvc-jsp</artifactId> <version>3.1.9</version> </dependency>
and for non-Maven users the list of dependencies is available at jersey-mvc-jsp.
To add support for other (custom) templating engines into Jersey MVC Templating facility, you need to implement the TemplateProcessor and register this class into your application.
When writing template processors it is recommend that you use an appropriate unique suffix for the processable template references, in which case it is then possible to easily support mixing of multiple templating engines in a single application without conflicts.
Example 22.13. Custom TemplateProcessor
@Provider class MyTemplateProcessor implements TemplateProcessor<String> { @Override public String resolve(String path, final MediaType mediaType) { final String extension = ".testp"; if (!path.endsWith(extension)) { path = path + extension; } final URL u = this.getClass().getResource(path); return u == null ? null : path; } @Override public void writeTo(String templateReference, Viewable viewable, MediaType mediaType, OutputStream out) throws IOException { final PrintStream ps = new PrintStream(out); ps.print("path="); ps.print(templateReference); ps.println(); ps.print("model="); ps.print(viewable.getModel().toString()); ps.println(); } }
Example 22.14. Registering custom TemplateProcessor
new ResourceConfig() .register(MyTemplateProcessor.class) // Further configuration of ResourceConfig. .register( ... );
In a typical set-up projects using the Jersey MVC templating support would depend on the base module that
provides the API and SPI and a single templating engine module for the templating engine of your choice.
These modules need to be mentioned explicitly in your pom.xml
file.
If you want to use just templating API infrastructure provided by Jersey for the MVC templating support in order to implement your custom support for a templating engine other than the ones provided by Jersey, you will need to add the base jersey-mvc module into the list of your dependencies:
<dependency> <groupId>org.glassfish.jersey.ext</groupId> <artifactId>jersey-mvc</artifactId> <version>3.1.9</version> </dependency>
To see an example of MVC (JSP) templating support in Jersey refer to the MVC (Bookstore) Example.