Web server

RESThub Web Server module is designed for REST webservices development. Both JSON (default) and XML serialization are supported out of the box.

It provides some abstract REST controller classes, and includes the following dependencies:

RESThub exception resolver allow to map common exceptions (Spring, JPA) to the right HTTP status codes:

  • IllegalArgumentException -> 400
  • ValidationException -> 400
  • NotFoundException, EntityNotFoundException and ObjectNotFoundException -> 404
  • NotImplementedException -> 501
  • EntityExistsException -> 409
  • Any uncatched exception -> 500

Configuration

In order to use it in your project, add the following snippet to your pom.xml:

<dependency>
    <groupId>org.resthub</groupId>
    <artifactId>resthub-web-server</artifactId>
    <version>2.2.0</version>
</dependency>

In order to import the default configuration, your should activate the resthub-web-server Spring profile in your WebAppInitializer class:

XmlWebApplicationContext appContext = new XmlWebApplicationContext();
appContext.getEnvironment().setActiveProfiles("resthub-web-server", "resthub-mongodb");

Usage

RESThub comes with a REST controller that allows you to create a CRUD webservice in a few lines. You have the choice to use a 2 layers (Controller -> Repository) or 3 layers (Controller -> Service -> Repository) software design.

You can find more details about these generic webservices, including their REST API description, on RESThub Javadoc.

2 layers software design

@Controller @RequestMapping("/repository-based")
public class SampleRestController extends RepositoryBasedRestController<Sample, Long, WebSampleResourceRepository> {

    @Override @Inject
    public void setRepository(WebSampleResourceRepository repository) {
        this.repository = repository;
    }
}

3 layers software design

@Controller @RequestMapping("/service-based")
public class SampleRestController extends ServiceBasedRestController<Sample, Long, SampleService> {

    @Override @Inject
    public void setService(SampleService service) {
        this.service = service;
    }
}

@Named("sampleService")
public class SampleServiceImpl extends CrudServiceImpl<Sample, Long, SampleRepository> implements SampleService {

    @Override @Inject
    public void setRepository(SampleRepository SampleRepository) {
        super.setRepository(SampleRepository);
    }
}

Sluggable controller

By default, generic controller use the database identifier (table primary key for JPA on MongoDB ID) in URLs to identify a resource. You can change this behaviour by overriding controller implementations to use the field you want. For example, this is common to use a human readable identifier called reference or slug to identify a resource. You can do that with generic repositories only by overriding findById() controller method:

@Controller @RequestMapping("/sample")
public class SluggableSampleController extends RepositoryBasedRestController<Sample, String, SampleRepository> {
    @Override @Inject
    public void setRepository(SampleRepository repository) {
        this.repository = repository;
    }

    @Override
    public Sample findById(@PathVariable String id) {
        Sample sample = this.repository.findBySlug(id);
        if (sample == null) {
            throw new NotFoundException();
        }
        return sample;
    }
}

With default behaviour we have URL like GET /sample/32. With sluggable behaviour we have URL lke GET /sample/niceref.

Be aware that when you override a Spring MVC controller method, your new method automatically reuse method level annotations from parent classes, but not parameter level annotations. That's why you need to specify parameters annotations again in order to make it work, like in the previous code sample.

Custom JSON Views

Spring MVC provides out-of-the-box support for returning your domain model in JSON, using Jackson under the covers.

Usual use cases for using custom JSON Views are :

  • Fix serialization issues in a flexible way (not like @JsonIgnore or @JsonBackReference annotation) for children-parent relations
  • Avoid loading too much data when used with JPA lazy loading + OpenSessionInView filter
  • Sometimes avoid to send some information to the client, for example a password field for a User class (needed in BO but not in FO for security reasons)

In order to use it, just add one or more JsonView interfaces (usually declared in the same java file than your domain class), in our case SummaryView. Please have a look to Jackson JsonView documentation for more details.

public class Book {
    @JsonView(SummaryView.class)
    private Integer id;

    private String title;

    @JsonView(SummaryView.class)
    private String author;

    private String review;

    public static interface SummaryView {}
}

Usage for the @JsonView is activated on a per controller method or class basis with the Jackson @JsonView annotation like bellow :

@RequestMapping("{id}/summary")
@JsonView(Book.SummaryView.class)
public @ResponseBody Book getSummary(@PathVariable("id") Integer id) {
    return data.get(id - 1);
}

@RequestMapping("{id}")
public @ResponseBody Book getDetail(@PathVariable("id") Integer id) {
    return data.get(id - 1);
}

The first method getSummary() will only serialize id and author properties, and getDetail() will serialize all properties. It also work on collection (List<Book> for example).

Register Jackson Modules

Jackson allows to register modules to enhance its capabilities (e.g. JodaTime module). Resthub provides a simple mean to register modules to the Jackson object mapper in the spring context:

    <bean id="jacksonModules" parent="resthubJacksonModules">
        <property name="sourceList">
            <array merge="true" >
                <value type="java.lang.Class">com.fasterxml.jackson.datatype.joda.JodaModule</value>
            </array>
        </property>
    </bean>

Note that parent="resthubJacksonModules" is mandatory

Model and DTOs with ModelMapper

The previous SluggableSampleController example shows one thing: when your application starts to grow, you usually want to address some specific needs:

  • tailoring data for your client (security, performance…)
  • changing your application behaviour without changing service contracts with your clients

For that, you often need to decorrelate serialized objects (DTOs) from your model.

RESThub suggests ModelMapper 0.7.2 as an optional dependency in resthub-common module.

You can include it in your project pom.xml:

<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper</artifactId>
    <version>0.7.2</version>
</dependency>

<dependency>
    <groupId>org.modelmapper.extensions</groupId>
    <artifactId>modelmapper-spring</artifactId>
    <version>0.7.2</version>
</dependency>

and then use it in your project:

ModelMapper modelMapper = new ModelMapper();
UserDTO userDTO = modelMapper.map(user, UserDTO.class);

Modelmapper has sensible defaults and can often map objects without additional configuration. For specific needs, you can use property maps.

Client logging

In order to make JS client application debugging easier, RESThub provides a webservice used to send client logs to the server. In order to activate it, you should enable the resthub-client-logging Spring profile.

POST api/log webservice expect this kind of body:

{"level":"warn","message":"log message","time":"2012-11-13T08:18:52.972Z"}

POST api/logs webservice expect this kind of body:

[{"level":"warn","message":"log message 1","time":"2012-11-13T08:18:53.342Z"},
{"level":"info","message":"log message 1","time":"2012-11-13T08:18:52.972Z"}]

Exception Mapping

You should add your own Exception handlers in order to handle your application custom exceptions by using @ControllerAdvice (will be scan like a bean in your classpath) and @ExceptionHandler annotations :

@ControllerAdvice
public class ResthubExceptionHandler extends ResponseEntityExceptionHandler {
    @ExceptionHandler(value={
            MyFirstException.class,
            MySecondException.class
    })
    public ResponseEntity<Object> handleCustomException(Exception ex, WebRequest request) {
        // ...

        return new ResponseEntity<Object>(body, headers, status);
    }
}

CORS Filter

RESThub includes an external CORS filter to allow communication between front and back on different domains. CORS Filter is provided by a third party library: http://software.dzhuvinov.com/cors-filter.html, you can find exhaustive configuration options here but we provide below some minimalistic configuration samples.

Configuration below defines a filter on your webapp allowing any request from domain http://example.com for any HTTP method in OPTIONS, GET, POST, PUT, DELETE, HEAD and allow headers Accept and Content-Type.

  • in a WebappInitializer (recommended)
public class WebAppInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        ...

        FilterRegistration corsFilter = servletContext.addFilter("cors", CORSFilter.class);
        corsFilter.addMappingForUrlPatterns(null, false, "/*");
        corsFilter.setInitParameter("cors.allowOrigin", "http://example.com");
        corsFilter.setInitParameter("cors.supportedMethods", "OPTIONS, GET, POST, PUT, DELETE, HEAD");
        corsFilter.setInitParameter("cors.supportedHeaders", "Accept, Content-Type");

        ...
    }
}
  • in a web.xml

...

<filter>
    <filter-name>CORSFilter</filter-name>
    <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>

    <init-param>
            <param-name>cors.allowOrigin</param-name>
            <param-value>http://example.com</param-value>
            <param-name>cors.supportedMethods</param-name>
            <param-value>OPTIONS, GET, POST, PUT, DELETE, HEAD</param-value>
            <param-name>cors.supportedHeaders</param-name>
            <param-value>Accept, Content-Type</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>CORSFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

...