Seam REST is a lightweight module that provides additional integration of technologies within the Java EE platform as well as third party technologies.
Seam REST is independent from CDI and JAX-RS implementations and thus fully portable between Java EE 6 environments.
The Seam REST module runs only on Java EE 6 compliant servers such as JBoss Application Server or GlassFish .
To use the Seam REST module, add
seam-rest
and
seam-rest-api
jars into the web application.
If using Maven, add the following
dependency
into the web application's
pom.xml
configuration file.
Example 1.1. Dependency added to pom.xml
<dependency>
<groupId>org.jboss.seam.rest</groupId>
<artifactId>seam-rest-api</artifactId>
<version>${seam.rest.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.seam.rest</groupId>
<artifactId>seam-rest-impl</artifactId>
<version>${seam.rest.version}</version>
</dependency>
Substitute the expression ${seam.rest.version} with the most recent or appropriate version of Seam Catch. Alternatively, you can create a Maven user-defined property to satisfy this substitution so you can centrally manage the version.
Besides, Seam REST has several transitive dependencies (which are added automatically when using maven). Refer to Table 6.1, “Transitive dependencies” for more details.
The Seam REST module registers
SeamExceptionMapper
to hook into the exception processing mechanism of JAX-RS and
TemplatingMessageBodyWriter
to provide templating support.
These components are registered by default if classpath scanning
of JAX-RS resources and providers is enabled (an empty
javax.ws.rs.core.Application
subclass is provided).
@ApplicationPath("/api/*")
public class MyApplication extends Application {}
Otherwise, if the Application's
getClasses()
method is overriden to select resources and providers explicitlyy
add
SeamExceptionMapper
and
TemplatingMessageBodyWriter
.
@ApplicationPath("/api/*")
public class MyApplication extends Application
{
@Override
public Set<Class<?>> getClasses()
{
Set<Class<?>> classes = new HashSet<Class<?>>();
...
...
...
classes.add(SeamExceptionMapper.class);
classes.add(TemplatingMessageBodyWriter.class);
return classes;
}
}
The JAX-RS specification defines the mechanism for exception mapping providers as the standard mechanism for Java exception handling. The Seam REST module comes with an alternative approach, which is more consistent with the CDI programming model. It is also easier to use and still remains portable.
The Seam REST module allows you to:
integrate with Seam Catch and thus handle exceptions that occur in different parts of an application uniformly;
define exception handling rules declaratively with annotations or XML.
Seam Catch handles exceptions within the Seam REST module: as result, an exception that occurs during an invocation of a JAX-RS service is routed through the Catch exception handling mechanism similar to the CDI event bus. This allows you to implement the exception handling logic in a loosely-coupled fashion.
The following code sample demonstrates a simple exception handler
that converts the
NoResultException
exception to a 404 HTTP response.
Example 2.1. Seam Catch Integration - NoResultException handler
@HandlesExceptions
public class ExceptionHandler
{
@Inject @RestResourceResponseBuilder builder
public void handleException(@Handles @RestRequest CaughtException<NoResultException> event)
{
builder.status(404).entity("The requested resource does not exist.");
}
}
The
| |
The
| |
A method for handling
|
Similarly to the CDI event bus, exceptions handled by a handler
method can be filtered by qualifiers. The example above treats
only exceptions that occur in a JAX-RS service
invocation (as opposed to all exceptions of the given type that
occur in the application, for example in the view layer). Thus, the
@RestRequest
qualifier is used to enable the handler only for exceptions that occur
during JAX-RS service invocation.
Catch integration is optional and only enabled when Catch libraries are available on classpath. For more information on Seam Catch, refer to Seam Catch reference documentation .
Exception-mapping rules are often fairly simple. Thus, instead of being implemented programatically, they can be expressed declaratively through metadata such as Java annotations or XML. The Seam REST module supports both ways of declarative configurations.
For each exception type, you can specify a status code and an error message of the HTTP response.
You can configure Seam REST exception mapping directly in your Java code
with Java Annotations.
An exception mapping rule is defined as a
@ExceptionMapping
annotation. Use an
@ExceptionMapping.List
annotation to define multiple
exception mappings.
Example 2.2. Annotation-based exception mapping configuration
@ExceptionMapping.List({
@ExceptionMapping(exceptionType = NoResultException.class, status = 404, message = "Requested resource does not exist."),
@ExceptionMapping(exceptionType = IllegalArgumentException.class, status = 400, message = "Illegal argument value.")
})
@ApplicationPath("/api")
public MyApplication extends Application {
The
@ExceptionMapping
annotation can be applied on any Java class in the deployment.
However, it
is recommended to keep all exception mapping
declarations in the same place, for example, in the
javax.ws.rs.core.Application
subclass.
Table 2.1. @ExceptionMapping properties
Name | Required | Default value | Description |
---|---|---|---|
exceptionType | true | - | Fully-qualified class name of the exception class |
status | true | - | HTTP status code |
message | false | - | Error message sent within the HTTP response |
useExceptionMessage | false | false | Exception error message |
interpolateMessageBody | false | true | Enabling/disabling the EL interpolation of the error message |
useJaxb | false | true | Enabling/disabling wrapping of the error message within a JAXB object. This allows marshalling to various media formats such as application/xml, application/json, etc. |
As an alternative to the annotation-based configuration, you can use the Seam Config module to configure the
SeamRestConfiguration
class in XML.
First, add the Seam Config module to the application. If you are using maven, you can do this by specifying the following dependency:
Example 2.3. Seam XML dependency added to the pom.xml file.
<dependency>
<groupId>org.jboss.seam.config</groupId>
<artifactId>seam-config-xml</artifactId>
<version>${seam.config.version}</version>
</dependency>
For more information on the Seam Config module, refer to the
Seam Config reference documentation
.
Once you have added the Seam XML module, specify the configuration in the
seam-beans.xml
file, located in the
WEB-INF
or
META-INF
folder of the web archive.
Example 2.4. Exception mapping configuration in seam-beans.xml
<rest:SeamRestConfiguration>
<rest:mappings>
<s:value>
<rest:Mapping exceptionType="javax.persistence.NoResultException" statusCode="404">
<rest:message>Requested resource does not exist.</rest:message>
</rest:Mapping>
</s:value>
<s:value>
<rest:Mapping exceptionType="java.lang.IllegalArgumentException" statusCode="400">
<rest:message>Illegal value.</rest:message>
</rest:Mapping>
</s:value>
</rest:mappings>
</rest:SeamRestConfiguration>
Furthermore, you can use EL expressions in message templates to provide dynamic and more descriptive error messages.
Example 2.5. Exception mapping configuration in seam-beans.xml
<rest:Mapping exceptionType="javax.persistence.NoResultException" statusCode="404">
<rest:message>Requested resource (#{uriInfo.path}) does not exist.</rest:message>
</rest:Mapping>
When an exception occurs at runtime, the
SeamExceptionMapper
first looks for a matching exception mapping rule.
If it finds one, it creates an HTTP response with the specified
status
code and error message.
The error message is marshalled within a JAXB object and is thus available in multiple media formats. The most commonly used formats are XML and JSON. Most JAX-RS implementations provide media providers for both of these formats. In addition, the error message is also available in plain text.
Example 2.6. Sample HTTP response
HTTP/1.1 404 Not Found
Content-Type: application/xml
Content-Length: 123
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<error>
<message>Requested resource does not exist.</message>
</error>
Bean Validation (JSR-303) is a specification introduced as a part of Java EE 6. It aims to provide a standardized way of validating the domain model across all application layers.
The Seam REST module follows the Bean Validation specification and the incomming HTTP requests can be validated with this standardized mechanism.
Firstly, enable the
ValidationInterceptor
in the
beans.xml
configuration
file.
<interceptors>
<class>org.jboss.seam.rest.validation.ValidationInterceptor</class>
</interceptors>
Then, enable validation of a particular method by decorating
it with
the
@ValidateRequest
annotation.
@PUT
@ValidateRequest
public void updateTask(Task incommingTask)
{
...
}
Now, the HTTP request's entity body (the incomingTask parameter) will be validated prior to invoking the method.
By default, the entity parameter (the parameter with no annotations
that represent the body of the HTTP request) is
validated. If the
object is valid, the web service method is
executed.
Otherwise, a
ValidationException
exception
is thrown.
The
ValidationException
exception
is a simple carrier of constraint violations found by the
Bean
Validation provider. The exception can be handled by an
ExceptionMapper
or Seam Catch handler.
Seam REST comes with a built-in
ValidationException
handler,
which is registered by default. The exception handler converts the
ValidationException
to an HTTP response with the 400 (Bad request) status code. Furthermore,
it sends messages relevant to the violated constraints within the
message body of the HTTP response.
Example 3.1. HTTP response
HTTP/1.1 400 Bad Request
Content-Type: application/xml
Content-Length: 129
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<error>
<messages>
<message>Name length must be between 1 and 100.</message>
</messages>
</error>
Besides the message body, the JAX-RS specification allows various parts of the HTTP request to be injected into the JAX-RS resource or passed as method parameters. These parameters are usually HTTP form parameters, query parameters, path parameters, headers, etc.
Example 3.2. JAX-RS resource
public class PersonResource
{
@QueryParam("search")
@Size(min = 1, max = 30)
private String query;
@QueryParam("start")
@DefaultValue("0")
@Min(0)
private int start;
@QueryParam("limit")
@DefaultValue("20")
@Min(0) @Max(50)
private int limit;
...
If a method of a resource is annotated with an
@ValidateRequest
annotation, the fields
of a resource are validated by default.
Since the JAX-RS injection occurs only at resource creation time,
do
not use the
JAX-RS field injection for other than
@RequestScoped
resources.
The JAX-RS specification allows path parameters, query parameters, matrix parameters, cookie parameters and headers to be passed as parameters of a resource method.
Example 3.3. JAX-RS method parameters
@GET
public List<Person>search(@QueryParam("search") String query,
@QueryParam("start") @DefaultValue("0") int start,
@QueryParam("limit") @DefaultValue("20") int limit)
Currently, Seam REST validates only JavaBean parameters (as oposed to primitive types, Strings and so on). Therefore, to validate these types of parameters, either use resource field validation described in Section 3.1.2, “Validating resource fields” or read further and use parameter objects.
In order to prevent an oversized method
signature when the number of
parameters is too large, JAX-RS implementations provide
implementations of the
Parameter Object pattern
. These objects aggregate multiple parameters into a single
object, for example
RESTEasy Form Object
or
Apache CXF Parameter Bean
.
These parameters can be validated by Seam REST. To trigger the validation, annotate the parameter with a
javax.validation.Valid
annotation.
Example 3.4. RESTEasy parameter object
public class MyForm {
@FormParam("stuff")
@Size(min = 1, max = 30)
private int stuff;
@HeaderParam("myHeader")
private String header;
@PathParam("foo")
public void setFoo(String foo) {...}
}
@POST
@Path("/myservice")
@ValidateRequest
public void post(@Valid @Form MyForm form) {...}
Table 3.1. @ValidateRequest annotation properties
@ValidateRequest attribute | Description | Default value |
---|---|---|
validateMessageBody | Enabling/disabling validation of message body parameters | true |
validateResourceFields | Enabling/disabling validation of fields of a JAX-RS resource | true |
groups | Validation groups to be used for validation | javax.validation.groups.Default |
In some cases, it is desired to have a specific group of constraints used for validation of web service parameters. These constraints are usually weaker than the default constraints of a domain model. Take partial updates as an example.
Consider the following example:
Example 3.5. Employee.java
public class Employee {
@NotNull
@Size(min = 2, max = 30)
private String name;
@NotNull
private String email;
@NotNull
private Department department;
// getters and setters
}
The Employee resource in the example above is not allowed to have the null value specified in any of its fields. Thus, the entire representation of a resource (including the department and related object graph) must be sent to update the resource.
When using partial updates, only values of modified fields are required to be sent within the update request, while the non-null values of the received object are updated. Therefore, two groups of constraints are needed: group for partial updates (including @Size and @Email, excluding @NotNull) and the default group (@NotNull).
A validation group is a simple Java interface:
Example 3.7. Employee.java
@GroupSequence({ Default.class, PartialUpdateGroup.class })
public class Employee {@NotNull
@Size(min = 2, max = 30, groups = PartialUpdateGroup.class)
private String name;
@NotNull
@Email(groups = PartialUpdateGroup.class)
private String email;
@NotNull
private Department department;
// getters and setters
}
The
| |
The
| |
The
|
Finally, the
ValidationInterceptor
is configured to validate the
PartialUpdateGroup
group
only.
Example 3.8. EmployeeResource.java
@Path("/{id}")
@PUT
@Consumes("application/xml")@ValidateRequest(groups = PartialUpdateGroup.class)
public void updateEmployee(Employee e, @PathParam("id") long id)
{
Employee employee = em.find(Employee.class, id);if (e.getName() != null)
{
employee.setName(e.getName());
}
if (e.getEmail() != null)
{
employee.setEmail(e.getEmail());
}
}
The partial update validation group is used for web service parameter validation. | |
Partial update — only the not-null fields of the transferred representation are used for update. The null fields are not updated. |
Seam REST allows to create HTTP responses based on the defined templates. Instead of being bound to a particlar templating engine, Seam REST comes with a support for multiple templating engines and support for others can be plugged in.
REST-based web services are often expected to return multiple representations of a resource. The templating support is useful for producing media formats such as XHTML and it can be also used instead of JAXB to produce domain-specific XML representations of a resource. Besides, almost any other representation of a resource can be described in a template.
To enable templating for a particular method, decorate the method with the
@ResponseTemplate
annotation. Path to a template file to be used for rendering is required.
Example 4.1. @ResponseTemplate in action
@ResponseTemplate("/freemarker/task.ftl")
public Task getTask(@PathParam("taskId") long taskId) {
...
}
The
@ResponseTemplate
annotation offers several other options. For example, it is possible for a method
to offer multiple representations of a resource, each rendered with a different template.
In the example below, the
produces
member of the
@ResponseTemplate
annotation is used
to distinguish between produced media types.
Example 4.2. Multiple @ResponseTemplates
@GET
@Produces( { "application/json", "application/categories+xml", "application/categories-short+xml" })
@ResponseTemplate.List({
@ResponseTemplate(value = "/freemarker/categories.ftl", produces = "application/categories+xml"),
@ResponseTemplate(value = "/freemarker/categories-short.ftl", produces = "application/categories-short+xml")
})
public List<Category> getCategories()
Table 4.1. @ResponseTemplate options
Name | Required | Default value | Description |
---|---|---|---|
value | true | - | Path to the template (for example /freemarker/categories.ftl) |
produces | false | */* | Restriction of media type produced by the template (useful in situations when a method produces multiple media types, with different templates) |
responseName | false | response | Name under which the object returned by the JAX-RS method is available in the template (for example, Hello ${response.name}) |
There are several ways of accessing the domain data within a template.
Firstly, the object returned by the JAX-RS method is available under the "response" name by default.
The object can be made available under a different name using the
responseName
member of the
@ResponseTemplate
annotation.
Secondly, every bean reachable via an EL expression is available within a template.
Example 4.4. Using EL names in a template
#foreach(${student} in ${university.students})
<student>${student.name}</student>
#end
Note that the syntax of the expression depends on the particular
templating engine and mostly differs from the syntax of EL expressions. For example,
${university.students}
must be used instead of
#{university.students}
in a FreeMarker template.
Last but not least, the model can be populated programatically. In order to do that, inject the
TemplatingModel
bean and put the desired objects into the underlying
data
map. In the following example, the list of professors is available under the "professors" name.
Example 4.5. Defining model programatically
@Inject
private TemplatingModel model;
@GET
@ResponseTemplate("/freemarker/university.ftl")
public University getUniversity()
{
// load university and professors
University university = ...
List<Professor> professors = ...
model.getData().put("professors", professors);
return university;
}
Seam REST currently comes with built-in templating providers for FreeMarker and Apache Velocity.
FreeMarker is one of the most popular templating engines. To enable Seam REST FreeMarker support, bundle the FreeMarker jar with the web application.
For more information on writing FreeMarker templates, refer to the FreeMarker Manual .
Apache Velocity is another popular Java-based templating engine. Similarly to FreeMarker support, Velocity support is enabled automatically if Velocity libraries are detected on the classpath.
For more information on writing Velocity templates, refer to the Apache Velocity User Guide
All that needs to be done to extend the set of supported templating engines is to implement
the
TemplatingProvider
interface. Refer to
Javadoc
for hints.
In certain deployment scenarios it is not possible to control the classpath completely and multiple template engines may be available at the same time. If that happens, Seam REST fails to operate with the following message:
Multiple TemplatingProviders found on classpath. Select the prefered one.
In such case, define the prefered templating engine in the XML configuration as demonstrated below to resolve the TemplatingProvider ambiguity.
Example 4.6. Prefered provider
<beans xmlns:rest="urn:java:org.jboss.seam.rest:org.jboss.seam.rest.exceptions">
<rest:SeamRestConfiguration preferedTemplatingProvider="org.jboss.seam.rest.templating.freemarker.FreeMarkerProvider">
</beans>
Table 4.2. Built-in templating providers
Name | FQCN |
---|---|
FreeMarker | org.jboss.seam.rest.templating.freemarker.FreeMarkerProvider |
Apache Velocity | org.jboss.seam.rest.templating.velocity.VelocityProvider |
The RESTEasy Client Framework is a framework for writing clients for REST-based web services. It reuses JAX-RS metadata for creating HTTP requests. For more information about the framework, refer to the project documentation .
Integration with the RESTEasy Client Framework is optional in Seam REST and only available when RESTEasy is available on classpath.
Let us assume as an example that a remote server exposes a web service for providing task details to the client
through the
TaskService
interface below.
Example 5.1. Sample JAX-RS annotated interface
@Path("/task")
@Produces("application/xml")
public interface TaskService
{
@GET
@Path("/{id}")
Task getTask(@PathParam("id")long id);
}
To access the remote web service, Seam REST builds and injects a client object of the web service.
Example 5.2. Injecting REST Client
@Inject @RestClient("http://example.com")
private TaskService taskService;
...
Task task = taskService.getTask(1);
The Seam REST module injects a
proxied
TaskService
interface and the RESTEasy Client
Framework converts every method invocation on the
TaskService
to an
HTTP request and sends it over the wire to
http://example.com
. The
HTTP response is unmarshalled automatically and the response object
is returned by the method call.
URI definition supports EL expressions.
@Inject @RestClient("#{example.service.uri}")
Besides proxying JAX-RS interfaces, the RESTEasy Client Framework provides the ClientRequest API for manual building of HTTP requests. For more information on the ClientRequest API, refer to the project documentation .
Example 5.3. Injecting ClientRequest
@Inject @RestClient("http://localhost:8080/test/ping")
private ClientRequest request;
...
request.accept(MediaType.TEXT_PLAIN_TYPE);
ClientResponse<String> response = request.get(String.class);
If not specified otherwise, every request is executed by the default Apache HTTP Client 4 configuration. This can be altered by providing a ClientExecutor bean.
Example 5.4. Custom Apache HTTP Client 4 configuration
@Produces
public ClientExecutor createExecutor()
{
HttpParams params = new BasicHttpParams();
ConnManagerParams.setMaxTotalConnections(params, 3);
ConnManagerParams.setTimeout(params, 1000);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
HttpClient httpClient = new DefaultHttpClient(cm, params);
return new ApacheHttpClient4Executor(httpClient);
}
The Seam REST module depends on the transitive dependencies at runtime listed in table Table 6.1, “Transitive dependencies” .
Seam Catch can be used for handling Java exceptions. For more information on using Seam Catch with Seam REST, refer to Section 2.1, “Seam Catch Integration”
<dependency>
<groupId>org.jboss.seam.catch</groupId>
<artifactId>seam-catch-api</artifactId>
<version>${seam.catch.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.seam.catch</groupId>
<artifactId>seam-catch-impl</artifactId>
<version>${seam.catch.version}</version>
</dependency>
Seam Config can be used to configure Seam REST using XML. For more information on using Seam Config with Seam REST, refer to Section 2.2.2, “XML configuration”
<dependency>
<groupId>org.jboss.seam.config</groupId>
<artifactId>seam-config-xml</artifactId>
<version>${seam.config.version}</version>
</dependency>
FreeMarker can be used for rendering HTTP responses. For more information on using FreeMarker with Seam REST, refer to Section 4.2.1, “FreeMarker”
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>${freemarker.version}</version>
</dependency>
Apache Velocity can be used for rendering HTTP responses. For more information on using Velocity with Seam REST, refer to Section 4.2.2, “Apache Velocity”
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>${velocity.version}</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-tools</artifactId>
<version>${velocity.tools.version}</version>
</dependency>
RESTEasy Client Framework can be used for building clients of RESTful web services. For more information on using RESTEasy Client Framework, refer to Chapter 5, RESTEasy Client Framework Integration
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>${resteasy.version}</version>
</dependency>
Note that RESTEasy is provided on JBoss Application Server 6 and thus you do not need to bundle it with the web application.