The GREB (Generic Resource Entity Builder) API module enables you to provide a simple, flexible JSON data producing application using the standard HstComponent code.
(By courtesy of Ferran Pestaña, licensed by CC BY-SA 2.0)
Suppose you simply want to produce content data in JSON output for any REST API clients
(single page applicaton or server-side application) quickly in a very flexible way without having to
be concerned about considering standards such as JAX-RS or any other restricitions. Also, suppose you
or your colleages already know how to implement standard HstComponents and retrieve content there and
so you simply want to implement even RESTful services using the same technology, HstComponents.
In this case, GREB (Generic Resource Entity Builder) API can be a good technical choice.
Please see Install page to install this addon module onto your project. Also see the Release Notes to find the proper version for your project.
GREB API is simply a new built-in HST-2 Pipeline to do almost the same things as the default HST-2 Site
Pipeline, except of the following:
Pipeline of GREB API consists of the same Valves, but overrides the
AggregationValve to produce JSON output instead of the default (HTML) output.
HstComponent, it doesn't executes
all the rendering templates of each HstComponent, After executing #doBeforeRender(HstRequest, HstResponse)
on each HstComponent, it stops processing and aggregates all the contributed resource entities and to
produce an aggregated JSON result to client.
HstComponent class can (optionally) contribute any resource entity objects by using GREB API in
its code (e.g, #doBeforeRender(HstRequest, HstResponse)). In the overriden AggregationValve, GREB API
will collect all the contributed resource entity objects based on the contribution paths and serialize
the resource entity objects by using Jackson (v2) library.
Here's an architectural view.
Bascially, you need to configure a separate mount (mapped to "hst:root/grebapi", for example) for GREB API,
under your default site pipeline (mapped to "hst:root", for example).
So the GREB API mount (e.g, "hst:root/grebapi") will inherit everything from the parent mount (e.g, "hst:root"),
including sitemap, pages, components, etc.
So, the GREB API mount and its Pipeline will use the same configuration and the same HstComponents.
When the GREB API mount is requested by a REST Client (e.g, http://localhost:8080/site/grebapi/a/b/c.html),
the delivery tier will invoke the same sitemap item, page and components, not templates.
After invoking #doBeforeRender(HstRequest, HstResponse) on each HstComponent,
it collects all the contributed resource entity objects from each HstComponent and aggregate the objects
into a JSON output to the REST Client.
You can contribute any objects (POJOs or any JSON-serializable objects by Jackson v2 library)
in HstComponent code or any other code involved in HST-2 request processing.
If you don't contribute any objects in your code (e.g, HstComponents),
the GREB API pipeline will produce an empty JSON object by default:
{
}
To contribute an object, you should get
com.onehippo.cms7.genericresource.entitybuilder.GenericResourceEntityBuilder instance first:
GenericResourceEntityBuilder builder = GenericResourceEntityBuilder.get(RequestContextProvider.get());
You can contribute any scalar values on specific paths like the folloiwng:
builder.addResourceEntity("title", document.getTitle());
builder.addResourceEntity("timestamp", System.currentTimeMillis());
As two objects were contributed with paths, then the JSON output will look like this:
{
"title": "Hello, World!",
"timestamp": 1479249799770
}
You can contribute any POJO or any Java objects that can be serialized to JSON by using Jackson v2 library:
builder.addResourceEntity("document", document);
Now, the JSON output will look like this:
{
"title": "Hello, World!",
"timestamp": 1479249799770,
"document": {
"title": "The medusa news",
"introduction": "This is a news article.",
// ...
}
}
If a Java object is contributed, then GREB API simply serialize it into JSON using Jackson v2 library. Therefore, you should make sure that your Java objects to contribute are serializable in Jackson v2 library. Consider using Jackson annotations if you want to exclude some properties or control the serializations.
You can wrap your resource entity objects in either java.util.Collection
(such as java.util.List or java.util.Set) or
java.util.Map like the following example:
List<Object> references = new LinkedList<>();
references.add(referenceDoc1);
references.add(referenceDoc2);
builder.addResourceEntity("references", references);
Map<String, Object> images = new LinkedHashMap<>();
images.put("portrait", portrait);
images.put("landscape", landscape);
builder.addResourceEntity("images", images);
Now, the JSON output will look like this:
{
"title": "Hello, World!",
"timestamp": 1479249799770,
"document": {
"title": "The medusa news",
"introduction": "This is a news article.",
// ...
},
"references": [
{
"title": "Referenced Document 1",
// ...
},
{
"title": "Referenced Document 2",
// ...
}
],
"images": {
"portrait": {
// SNIP
},
"landscape": {
// SNIP
}
}
}
You can nest objects as deep as you want through container
(either java.util.Collection or java.util.Map) objects.
You can also retrieve a resource entity object whether it is a scalar value, object value, collection or map value:
Object value = builder.getResourceEntity(name);
Collection<Object> collectionValue = builder.getCollectionResourceEntity(name);
Map<Object> mapValue = builder.getMapResourceEntity(name);
GREB API takes care of JSON serialization on built-in HST-2 objects such as:
HippoBean interface. e.g, HST-2 Content Beans, generated manually
or through Essentials for each document type, built-in HST-2 Content Beans for images, assets, folders, html, mirror, etc.
Therefore, you can contribute objects in those categories directly without having to wrap it to new POJO objects.
GREB API support customizing JSON serialization on specific beans by adding custom Jackson Mixins for the specific bean types.
In order to register your custom Jackson Mixin classes (or interfaces) with Jackson annotations,
override the following beans in an Spring Beans assembly overriding XML file under
site/components/src/resources/META-INF/hst-assembly/overrides/addon/com/onehippo/cms7/genericresource/entitybuilder/
folder (e.g, my-custom-mixins.xml) like the following example:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
<bean id="genericResourceEntityJacksonObjectMapperExtraMixins"
class="org.springframework.beans.factory.config.MapFactoryBean">
<property name="sourceMap">
<map>
<!-- Adding Jackson Mixin class, MyBeanMixin, for MyBean class type. -->
<entry key="org.example.beans.MyBean" value="org.example.beans.jackson.MyBeanMixin" />
<!-- SNIP -->
</map>
</property>
</bean>
</beans>
This also allows you to override the built-in default Jackson Mixins for built-in types
(such as HippoGalleryImageSetBean, HippoGalleryImageBean, etc.)
by your custom Jacksion Mixins.