January 10, 2016

Crudlet: Ready-to-use Restangular-to-SQL CRUD with JAX-RS



I’ve been searching for a lean, simple REST-to-SQL CRUD framework before I realized that it’s actually quite easy to implement that based on JAX-RS. I named the result Crudlet and published it open source such that you can use it either as a dependency to build your web application, or to inspect its code in order to build your own CRUD application with vanilla JAX-RS from scratch.

What I wanted to have is a server framework which provides with minimal development / deployment overhead:
  • CRUD operations on any relational database storage of my choice
  • Declarative definition of strongly-typed domain models with auto-ORM
  • Out-of-the-box JSON serialization / deserialization
  • Out-of-the-box validation error handling with I18N support
  • Built on production-ready components

Technology choice

Most of what I needed to build it is provided out-of-the-box by Java EE’s JAX-RS standard, as implemented by Java EE containers:
  • Declarative definition of REST endpoints with Java annotations
  • Declarative definition of the domain models with auto-ORM as Java POJOs
  • Out-of-the-box JSON serialization / deserialization
  • Out-of-the-box validation error handling
Being based on a Java EE / JAX-RS stack, this comes with a couple of advantages. Note that I don’t want to imply here that these ensuing technologies are bad, but that given the requirements I declared above, Java EE / JAX-RS seems a better match.

Compared to Node.js / Express

  • Vanilla Java is strongly typed (as opposed to vanilla JavaScript), with seems to suit well the requirement to build a stable domain model.
  • JAX-RS is all about convention over configuration where you can declaratively build your models, register components declaratively (a “plugin” approach) whereas writing an actual Node.js server from scratch typically forces us to imperatively write an actual server deamon.

Compared to a MongoDB database backend

Java EE allows us to work with any JDBC compliant persistence storage. I’ve specifically decided to use an SQL-based database because of some restrictions a document-based data storage such as MongoDB implies:
  • SQL supports schema validation (which is not the case with plain MongoDB)
  • SQL supports per-entity transaction control (which is not the case with plain MongoDB)

An example application

Now, I will use Crudlet to build a demo CRUD application. I will here recreate a use case I’ve used in previous blog examples.

I will build a very simple example web application with CRUD functionality for these two business entities:

Each entity is uniquely identified by its id which is auto-generated on persist.

A customer can have one or more payments, and one payment is associated with exactly one customer.

There will be a master view (list of all entities) and a detail view (edit page for current entity) for each entity type, respectively. The payments master view will be integrated in the detail view of the parent customer entity.

The rest of this tutorial is taken from Crudlet’s GitHub project usage information.

The complete source code of the example application (server and client) is available in a separate GitHub repository.

Server: Setup

JAX-RS

You need to setup the JAX-RS Application servlet in the web.xml file as shown in the demo project:

<servlet>
    <servlet-name>javax.ws.rs.core.Application</servlet-name>
</servlet>
<servlet-mapping>
    <servlet-name>javax.ws.rs.core.Application</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

Database

Define your database connection in the persistence.xml file. Any JDBC compliant connection is supported. In the demo project, we use a JTA data source the configuration of which is set up in the application server.

CORS

Crudlet by default allows you to handle CORS request without nasty errors as is usually desired in development / debug stage. The required request / response filters are implemented in the CorsRequestFilter and CorsResponseFilter class, respectively. Set the CorsRequestFilter#ALLOW_OPTIONS and CorsResponseFilter#ALLOW_CORS boolean flag to false (e.g. in a @Startup @Singleton EJB bean) to disable CORS allow-all policy.

Server: Implementation

Crudlet provides a simple, lean framework to build (optionally RESTful) CRUD JSF applications based on common best practices. Having a basic CRUD implementation in place means that you can an any entity type:

  • Create (C) new entities
  • Read (R) persistent entities from the persistence storage
  • Update (U) entities in the persistence storage
  • Delete (D) entities from the persistence storage

Building your application around a CRUD centric approach brings a couple of advantages:

  • The service interface is very simplistic, lean and self-documenting
  • The business logic resides in the model rather than in the service interface which matches well an object-oriented language like Java
  • Because the service interface stays the same for all entities, we can make excessive use of abstraction through inheritance and generics
  • This architecture matches well a best practices compliant RESTful implementation where the four CRUD actions are really matched against HTTP verbs.

This best practices architecture is based on three central artifacts for which Crudlet provides an abstract generic base implementation:

  • CrudEntity: the entity model
  • CrudService: the persistence service
  • CrudResource: the REST web service endpoint

In a CRUD application, the relation between these artifacts is 1 : 1 : 1; you will thus build a service and a controller for every entity. Thanks to the level of abstraction provided by Crudlet, this is a matter of about 30 lines of code:

  • CrudEntity makes sure your entity implements an auto-ID generation strategy
  • CrudService implements basic persistence storage access (through an EntityManager) for the four CRUD operations
  • CrudResource implements a REST web service endpoint for editing all entities in the persistence storage including out-of-the-box support for returning I18N-ready model validation error messages.

Entity

Use either the CrudIdentifiable interface or the CrudEntity class to derive your entity model classes from. This is the only prerequisite to use them with a CrudService and a CrudResource.

The difference between the interface and the class is that the latter provides an auto-generated Long id field implementation out-of-the-box.

For instance, to create a Customer entity:

@Entity
public class Customer extends CrudEntity { 
    @NotNull
    @Pattern(regexp = "[A-Za-z ]*")
    private String name;
    private String address;
    private String city;
    ...

Use Bean Validation constraints to declaratively specify the model validation.

Service

In order to create a CRUD service for an entity type, implement CrudService for the entity and register it as a CDI bean in the container (depending on beans.xml bean-discovery-mode, explicit registration may not be necessary).

For instance, to create the service for the Customer entity:

public class CustomerService extends CrudService<Customer> {
    @Override
    @PersistenceContext
    protected void setEm(EntityManager em) {
        super.setEm(em);
    }

    @Override
    public Customer create() {
        return new Customer();
    }

    @Override
    public Class<Customer> getModelClass() {
        return Customer.class;
    }
}
  • Within the setEm(EntityManager) method, simply call the super method. The important part is that you inject your @PersistenceContext in this method by annotation.

Of course, you are free to add additional methods to your CrudService implementation where reasonable.

Web service endpoint

Finally, create the REST web service endpoint by implementing CrudResource for the entity and register it as a @Stateless EJB bean in the container.

For instance, to create the web service endpoint for the Customer entity:

@Path("customers")
@Stateless
public class CustomerResource extends CrudResource<Customer> {
    @Inject
    private CustomerService service;

    @Override
    protected CrudService<Customer> getService() {
        return service;
    }
}
  • The @Path defines the base path of the web service endpoint.
  • Within the getService() method, return the concrete CrudService for the entity type in question which you should dependency-inject into the controller.

That’s it. Now you can use e.g. the httpie command line tool to verify that you can execute RESTful CRUD operations on your entity running on the database.

Of course, you are free to add additional methods to your CrudResource implementation where reasonable.

Read on for an example client implementation based on AngularJS.

AngularJS client: Setup

In this example, we use Restangular as an abstraction layer to do RESTful HTTP requests which offers a far more sophisticated although more concise API than AngularJS’s built-in $http and $resource. It is set up as shown in the demo application’s main JavaScript file:

.config(function (RestangularProvider) {
    RestangularProvider.setBaseUrl('http://localhost:8080/CrudletDemo.server/');

    RestangularProvider.setRequestInterceptor(function(elem, operation) {
        // prevent "400 - bad request" error on DELETE
        // as in https://github.com/mgonto/restangular/issues/78#issuecomment-18687759
        if (operation === "remove") {
            return undefined;
        }
        return elem;
    });
})

You also potentially want to install and setup the angular-translate module for I18N support:

.config(['$translateProvider', function ($translateProvider) {
    $translateProvider.translations('en', translations);
    $translateProvider.preferredLanguage('en');
    $translateProvider.useMissingTranslationHandlerLog();
    $translateProvider.useSanitizeValueStrategy('sanitize');
}])

AngularJS client: Implementation

In the “controller” JavaScript file, we can use Restangular to access the RESTful web service endpoint of our Crudlet Customer service like so:

  • Get a list of entities (GET /customers/): Restangular.all("customers").getList().then(function(entities) {...})
  • Get a single entity (GET /customers/1): Restangular.one("customers", $routeParams.id).get().then(function (entity) {...})
  • Save an entity (PUT /customers/1): $scope.entity.save().then(function() {...})

Validation

An interesting aspect of Crudlet is its out-of-the-box support for localized validation error messages. If upon save, a validation error occurs, the server answers e.g. like this:

{
    "validationErrors": {
        "name": {
            "attributes": {
                "flags": "[Ljavax.validation.constraints.Pattern$Flag;@1f414540",
                "regexp": "[A-Za-z ]*"
            },
            "constraintClassName": "javax.validation.constraints.Pattern",
            "invalidValue": "Name not allowed!!",
            "messageTemplate": "javax.validation.constraints.Pattern.message"
        }
    }
}

Using the angular-translate module of AngularJS we set up previously, we can show all localized validation messages like so:

<div class="alert alert-danger" ng-show="validationErrors != null">
    <ul>
        <li ng-repeat="(component, error) in validationErrors">
            {{'payment.' + component | translate}}: {{'error.' + error.messageTemplate | translate:error.attributes }}
        </li>
    </ul>
</div>

The validationErrors.<property>.messageTemplate part is the message template returned by the bean validation constraint. We can thus e.g. base the validation error localization on Hibernate’s own validation messages:

var translations = {
    ...
    'error.javax.validation.constraints.Pattern.message': 'must match "{{regexp}}"',
    ...
};

(I preceded it with error. here.)

Because the error object returned by the server is a map, we can also use it to conditionally render special error styling, e.g. using Bootstrap’s error style class:

ng-class="{'has-error': errors.amount != null}"

Exceptions

Similar to validation errors, some runtime exceptions will also return a user-friendly error response message. For instance, let’s assume that a Customer has a list of Payments and you try to delete a Customer with a non-empty Payments list:

{
    "error": {
        "detailMessage": "DELETE on table 'CUSTOMER' caused a violation of foreign key constraint 
            'PAYMENTCUSTOMER_ID' for key (1).  The statement has been rolled back.",
        "exception": "java.sql.SQLIntegrityConstraintViolationException"
    }
}

Again, you can catch and display these in the AngularJS view:

<div class="alert alert-danger" ng-show="errorNotFound != null || error != null">
    <ul>
        <li ng-show="error != null">
            {{'error.' + error.exception | translate}}
        </li>
    </ul>
</div>

With appropriate localization:

var translations = {
    ...
    'error.java.sql.SQLIntegrityConstraintViolationException': 'Cannot delete an object which is still referenced to by other objects.',
    ...
};

Because in a real-world production environment, exposing details of an exception may be a security issue, you can suppress this user-friendly exception detail output by setting the RestfulExceptionMapper#RETURN_EXCEPTION_BODY boolean flag to false.

For a complete example, please take a look at the example application. It also shows you how to easily implement a CrudResource for nested resources.

Conclusion

With Crudlet, I feel like I finally have a save starting point to build CRUD-based REST-to-SQL applications. In fact, CRUD is an ideal match for the REST pattern, and is a best practices compliant fundament for an application architecture.

As such, Crudlet is especially useful to kickstart an AngularJS / Restangular project (or really any web service client) where you’d like to concentrate on trying out / building front-end logic, assuming the database backend is “just there”, working as expected.

A lot of my experience, as documented in other blog posts, has actually inspired this framework:
In fact, the demo use case is copied from my AngularJS – MongoDB article. It’s interesting to see that with a minimal convention-over-configuration compliant Java EE server, we can actually overcome the restrictions of a direct MongoDB access I pointed out in that article.

(Just as a side note, I also reused the same original HTML markup, but I enhanced its original version with Bootstrap component styling using CrudFaces’ auto-styling abilities.)

Of course, the Crudlet implementation at its current stage is still very rough, and I hope to find time to make it more robust and actually production-ready in the near future. In the meantime, please don’t hesitate to let me know whether you find this project useful; please leave any improvement suggestions in the comments section below.

Again, feel free to check out the source code of the complete example application built on top of Crudlet as well.

Update February 26, 2016: Crudlet 0.2 is now officially released.

No comments:

Post a Comment