An Adaptive RESTful API is a library for automatic HTTP request handling based on the chain of filters. It's written in Java and is dedicated to use within the Java EE applications.
NOTICE: This project is a result of the author's work on his diploma thesis.
The basic idea behind the library is to process the HTTP request in the chain of filters represented by the Filter
class. It's a responsibility of the concrete filter to decide what to do with the request, if it handles completely (creates a response), or it makes something useful with the content (eq. converts JSON to POJO) of the request and then resigns the processing to the next filter in the chain. You can see an example flow of the request in this picture.
To be able to process the request through the filters in some manner, there are two basic concepts: model
and configuration
of this model. The model is used to describe your domain classes which you want to reveal in the API. Model consists of entities which contain properties (attributes and relationships).
The configuration allows you to customize a meaning of the each part in the model in a hierarchical way. It makes possible to use some global configuration applicable to the whole model and to refine each individual part of the model as required.
The library is divided into seperate modules. On the base level is core
module which depends on the meta
module. The rest of the modules add support for more concrete types of the filter. The example
module shows how to use the library in a "real world" application.
The meta
module is used to build the model of the entities with properties (attributes and relationships) and it's configuration. The inspection process is done by the inspector. The first task is to inspect concrete package
for classes in it. Then inspects triplets
of the field, it's getter (starts with is
or get
) and setter (starts with set
) methods. The inspector is not responsible for creating instances of the entities, nor propeties, but it delegates this work to a model listener. After the model is built, the configuration is inspected. The inspector goes through the model and asks configuration listener for the list of variables of the property, entity or model. The configuration respects the hierarchy of the model, so if you ask for some value on the attribute, it will flow through the hierarchy of configurations from the most specific to the most general configuration.
This module contains probably the most essesential part of the library, a filter. The filter is used to create the chain which defines the flow of the HTTP request goes through. Each filter accepts the HTTP context, model and configuration via the process method. In this method you decide what to do with the request, there are basically three options:
- set the response and stop chaining the process,
- do something with the request and resign the process to the next filter,
- throw an exception if something goes wrong.
This is an example implementation of the filter:
public class HelloFilter extends Filter {
@Override
public final HttpContext process(HttpContext httpContext, Model model, Configuration configuration) throws FilterException {
String requestContent = httpContext.getRequestContent();
if (requestContent.startsWith("Hello,")) {
String name = requestContent.substring("Hello,".length());
// set the content for next filter
httpContext.setContent(new Hello(name));
// resign the process to the next filter
return this.resign(httpContext, model, configuration);
} else {
throw new FilterException(HttpStatus.S_400); // bad request
}
}
}
There is no responsibility to create an HTTP context, model, nor configuration, it must be done somewhere else.
Adds default implementation of the filter for the caching and provides abstract methods to handle the request. If the load
method hits the cache, the filter returns the context immediately, otherwise it resigns the process and finally tries to save
the context.
This module contains simple implementation of the If-Modified-Since
caching mechanism.
The purpose of this module is to deal with content of the HTTP context. The dispatcher resolves an HTTP method, and the entity via the router, then it loads data handler from the configuration and delegates the process to him. There are available interfaces for GET, POST, PUT and DELETE methods.
The module also provides basic implementation of the data handlers for JPA's entity manager, see persistence package. It's not the responsibility of this module to create an entity manager.
The security is divided into the two parts:
Both abstract classes provides methods, where the security process should be handled. If the authentication, resp. authorization fails, then the appropriate exception must be thrown.
This module comes with an implementation of the HTTP Basic authentication.
The responsibility of this module is to serialize, resp. deserialize the content of the HTTP response, resp. request. The resolver loads the concrete serializer from configuration and delegates the (de)serialization process to him.
The JSON
(de)serializer and plain text
serializer are provided.
The FilteredServlet class implements service(request, response)
method to handle all HTTP communication. First off all, it asks for the ApplicationContext
singleton, which provides current model
and configuration
. The second step is to create a HttpContext from the HttpServletRequest
object. Then starts the processing through the filter's chain passing the httpContext
, model
and configuration
. Finally, it sets the HttpServletResponse
from processed HttpContent
or catched FilteredException
.
You use FilteredServlet
directly by creating an instance or subclassing it and setting the desired filter.
An example is built using a servlet implementation, so the initialization of the ApplicationContext
is done within the contextInitialized
method of the ServletContextListener
. The inspector uses these model and configuration listeners. The model of the application is defined here, it's a simple project/issue management system. The entity manager
is created by the PersistentContext, the usage of persistence handlers is shown in the configuration listener. The API is capable to communicate in the JSON format, and in a read-only mode of the plain text. The application is able to handle HTTP Basic authentication, method and serialization authorization (for more details look at the Users class and configuration listener).
So, an example request $ curl -u admin:1234 --header "Accept: application/json;charset=UTF-8" --header "Content-Type: application/json;charset=UTF-8" http://localhost:8080/Project/2
will end up to the response:
{
"started": false,
"id": 2,
"startedAt": null,
"name": "Project B",
"bugs": [ 2, 3 ],
"manager": 4,
"tasks": [ 3 ]
}
Adaptive RESTful API is available under the LGPL license. See the LICENSE file for more info.