Skip navigation links

Crudlet 0.1 API

A simple, lean JAX-RS based framework to build CRUD REST-to-SQL web applications running on a Java web application server, e.g. as an AngularJS backend.

See: Description

Packages 
Package Description
ch.codebulb.crudlet.config  
ch.codebulb.crudlet.model  
ch.codebulb.crudlet.model.errors  
ch.codebulb.crudlet.service  
ch.codebulb.crudlet.util  
ch.codebulb.crudlet.webservice  

A simple, lean JAX-RS based framework to build CRUD REST-to-SQL web applications running on a Java web application server, e.g. as an AngularJS backend.

From the project's GitHub repository's documentation:

Installation

Use JitPack to add its dependency to your Maven web application project:

<dependency>
    <groupId>com.github.codebulb</groupId>
    <artifactId>crudlet</artifactId>
    <version>0.1</version>
</dependency>
...
<repository>
    <id>jitpack.io</id>
    <url>https://jitpack.io</url>
</repository>

Replace the version by the tag / commit hash of your choice or -SNAPSHOT to get the newest SNAPSHOT.

Visit JitPack’s docs for more information.

Ways to use it

Why you should use it

Usage

Note: The complete source code of an 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 REST-to-SQL web applications based on common best practices. Having a basic CRUD implementation in place means that you can an any entity type:

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

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

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:

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;
    }
}

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;
    }
}

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:

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.

By example

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.

If you want to lean more about building RESTful web applications based on vanilla JAX-RS and Restangular, you may enjoy a blog post I’ve written about it; it features a complete example application as well.

Specification

REST service endpoints

Crudlet maps these HTTP requests to persistence storage operations:

Global hooks (overrides)

You can e.g. use a @Startup @Singleton EJB bean to manipulate the following values on application startup to configure application behavior:

Project status and future plans

Crudlet is currently experimental. I’d like to make some stability updates before releasing a proper 1.0 version. It may still already be useful for evaluation purposes, or as a skeleton to build your own solution.

This is a private project I’ve started for my own pleasure and usage and to learn more about building (Ajax) REST APIs, and I have no plans for (commercial) support.

You can also find more information about this project on its accompanying blog post and in the API docs.

Skip navigation links

Copyright © 2016, codebulb.ch. All rights reserved.