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:
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:
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.