HST Container Integration with Other Web Application Frameworks

Introduction

The delivery tier (a.k.a. HST) supports Container Level Integration with other External Web Application Frameworks. These include full-fledged MVC frameworks such as Spring Web MVC, Struts2, Cocoon, Wicket, Tiles2 and Sitemesh, but also a simple Filter/Servlet based handler module (e.g. even simple 'Hello, World!' style Servlets or JSP).

"Container Level Integration" means you can leverage HST URL Mappings (virtual host mappings, mount mappings and sitemap item mappings) and Content Mappings, without using HST Component Aggregation. Instead your existing web applications can just keep their own MVC application structure without having to migrate the existing components into HST Components, but they can leverage all the other features (HST URL/Content Mappings, Session Pooling, Content Beans/Query API, Link Rewriting, etc.).

HST Container Level Integration with External Web Application Frameworks is different from HST Component Bridging solutions such as Spring HST Component Bridge. The HST Component Bridging Solutions are basically executed by the DefaultSitePipeline and AggregationValve, and they are just an HST Component as part of the aggregated page. HST Container Level Integration with External Web Application on the other hand provides only URL/Content mapping and delegates the request to the external Web Application Framework without aggregating the page. The external Web Application Framework is fully responsible for the page rendering (or aggregation) and action handling.

For example, let's suppose you already have an existing Spring Web MVC Framework application connecting to a database to read/manage news article content with mounted URL paths like /news/, /news/2013/, /news/2013/08/, /news/2015/08/launching_new_product.html.

Now, you can keep the existing URL mapping inside your Spring Web MVC Framework configurations for the URLs, and you can also configure those URL mappings inside HST configurations to set content mappings. In that way the HST Container resolves the URL and Content mappings first, and delegates to the existing Spring Web MVC Framework application with resolved request context attributes.

By using the HST Container Level Integration with External Web Application Framework you can keep most existing controller/view and page aggregation code/configurations and simplify a lot of your content data access code by leveraging HST content mapping.

HST provides a transparent way to map URL and content for external web applications in the same way as for typical HST Component based applications, and it also gives a very simple API to access mapped content for external web applications.

Architectural Overview

In normal HST Component based applications, the DefaultSitePipeline gets invoked by the  HstFilter and RequestProcessor components as shown in the first rectangle (" Typical HST Page /Component Aggregation") of the following diagram.

 

//onehippo-prod.global.ssl.fastly.net/binaries/ninecolumn/content/gallery/connect/library/concepts/generic-webapp-integration.png

In the HST Container Level Integration with External Web Application Frameworks, WebApplicationInvokingPipeline gets invoked instead of DefaultSitePipeline.

The WebApplicationInvokingPipeline performs almost the same steps (valves) as  DefaultSitePipeline before AggregationValve, but it invokes FilterChainInvokingValve instead of AggregationValve. FilterChainInvokingValve simply invokes the  javax.servlet.FilterChain.doFilter(request, response) method, and so the next remaining Filters/Servlets of the external web application framework gets kicked in after the HST Container processes the URL and content mappings.

An HstRequestContext instance is instantiated by HstFilter and filled with required mapping information attributes and API components, so it can be used in both HST Components and any components inside an external Web MVC application. For example, as shown above in the simplified view, Controller or Model components can access the given HstRequestContext to get access to the resolved Virtual Host, Mount, Sitemap item, Content Beans and API components such as HstContentBeansTool or HstQueryManager.

 

How to Map an HST Sitemap Item or Mount to External Web Applications

Conceptually it is very simple. Please keep the following steps in mind:

  1. Your existing web application already has URL mappings.
    Assume you have an existing external Web (MVC) application which has some URL pattern mappings in /WEB-INF/web.xml or other the framework specific configuration (such as URL Handler Mappings in Spring Web MVC Framework).
  2. Configure the equivalent URL pattern of your web application in HST SITE configurations.  
    Configure the URL pattern through HST Sitemap Item or Mount configurations in the Hippo Repository. You can set relative content paths for each sitemap item too.
  3. Configure WebApplicationInvokingPipeline for the sitemap item or mount.  
    Now set WebApplicationInvokingPipeline as named pipeline to override the default pipeline configuration for the sitemap item or the mount. So the HST Container will process the URL and map to a specific sitemap item first, and let the WebApplicationInvokingPipeline invoke your web application.

Let's suppose you have a URL mapping for a Spring Web MVC application, defined in web.xml like the following:

<!-- Hereby an example Spring Web MVC application configuration goes.
     It's mapped by '/springapp/*' URL pattern below. -->
<servlet>
  <servlet-name>springapp</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <load-on-startup>10</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>springapp</servlet-name>
  <url-pattern>/springapp/*</url-pattern>
</servlet-mapping>

And suppose you have a Spring Web MVC Controller having annotated request mapping like this:

@Controller
@RequestMapping("/news")
public class NewsController {

    @RequestMapping(value={"", "/"})
    public ModelAndView listHandler(HttpServletRequest request) {
        ModelAndView mav = new ModelAndView("news/list");
        // SNIP...
        return mav;
    }

}

So far it is just a normal Spring Web Application configuration.

Integrate the HST Container into the existing Spring Web Application

Assume you want to delegate all requests for /springapp/ still to your Spring Web Application, but at the same time have the HST URL/Content mapping, JCR Session Pooling, Host / Channel information, HST Query API, etc available in your Spring Web Application, you can achieve that with the two very simple steps below:

Add a sitemap matcher for /springapp/ delegating to WebApplicationInvokingPipeline

Assume your sitemap looked something like:

+ hst:hst
  + hst:configurations
    + myproject
      + hst:sitemap
         + root
         |   - hst:relativecontentpath = homedocument
         + news
           - hst:relativecontentpath = news
           + _any_
               - hst:relativecontentpath = news/${1}

Assume all the above sitemap items have no specific pipeline configured, and thus invoke the default HST DefaultSitePipeline resulting in typical HST Page/Component aggregation. Adding a new sitemap item for the /springapp URLs invoking the WebApplicationInvokingPipeline is done as follows:

+ hst:hst
    + hst:configurations
        + myproject
            + hst:sitemap
               + root
               |   - hst:relativecontentpath = homedocument
               + news
               |   - hst:relativecontentpath = news
               |   + _any_
               |       - hst:relativecontentpath = news/${1}
               + springapp
                   - hst:namedpipeline = WebApplicationInvokingPipeline
                   + news
                       - hst:relativecontentpath = news
                       + _any_
                           - hst:relativecontentpath = news/${1}   
Note that the sitemap items news and _any_ below springapp inherited the hst:namedpipeline property value from springapp.

That is it. With the above sitemap, the URL /springapp/news will be mapped to the springapp/news sitemap item and URL paths in the pattern of /springapp/news/** will be mapped to the springapp/news/_any_ sitemap item.

For example, when a user makes a request to http://localhost:8080/site/springapp/news/2013/09/launching_new_product.html, then HST Container will resolve the request to the  springapp/news/_any_ sitemap item and sets it to the current HstRequestContext. HST will resolve the mapped content beans through the hst:relativecontentpath as well automatically by the configuration and make them available through the HstRequestContext. For example, if the request URL is http://localhost:8080/site/springapp/news/2013/09/launching_new_product.html, then the resolved relative content path can be /news/2013/09/launching_new_product document.

Now your existing web application code can retrieve HstRequestContext by calling the  RequestContextProvider.get() method and retrieve the mapped content bean by calling HstRequestContext.getContentBean() for instance, in any locations (Model, View or Controller code).

Running Spring Web MVC Framework Integration Example

Hippo TestSuite project contains a running Spring Web MVC Framework integration example. If you build/run the Hippo TestSuite project locally, then you can visit http://localhost:8080/site/springapp/news, which maps the Spring Web MVC Application URLs in the pattern of /springapp/** to the HST sitemap item ( springapp/**) to the Spring Web MVC Controller ( NewsController) in the end.

You can checkout the latest Hippo Testsuite tag from https://code.onehippo.org/cms-community/hippo-testsuite

And you can build/run with

mvn clean verify && mvn -P cargo.run

How to Access Mapped Content from External Web Applications

As mentioned above you can get access to HstRequestContext in almost every location in your external web application code. So it is possible to leverage any API provided by HstRequestContext just like typical HST Component based application.

For example you can use HstRequestContext#getContentBean() to access the mapped content bean for the resolved sitemap item.

HstRequestContext requestContext = RequestContextProvider.get();
HippoBean document = requestContext.getContentBean();
// now, you may pass the document bean to the view by
// putting it into the model object, for instance.

Or get hold of the root content for the current site

HstRequestContext requestContext = RequestContextProvider.get();
HippoBean siteRootContent = requestContext.getSiteContentBaseBean();

If you want to use the HST Query API in your Controller, and for example query for News documents, you can use

HstRequestContext requestContext = RequestContextProvider.get();
HstQuery query = requestContext.getQueryManager().createQuery(scope,
                                                              NewsBean.class);
HstQueryResult result = query.execute();

In your external web application code you can leverage almost everything provided by the HST container such as HST URL/Content Mappings, Session Pooling, Content Beans/ Query API, Link Rewriting, etc., in the same way a HST Component based application can.

HST Tag Libraries in your External Web Application

Basically all the HST tags with the exception of HST Component related ones (such as <hst:namespace/>, <hst:renderURL/>, <hst:actionURL/>, etc.) work without any problem even in the template pages of your existing external Web Application.

For example, you may use the  <hst:link/> tag or the  <hst:html/> tag in a view template JSP page of your existing Spring Web MVC application like the following example:

<%-- Generating a link to a sitemap item path --%>
<hst:link var="newsLink" path="/springapp/news" />
<a href="${newsLink}" title="News">News</a>

<%-- Rendering HTML content of the document by using
     <hst:html/> tag.
     The tag automatically rewrites all the internal
     links based on sitemap configuration. --%>
<div>
  <hst:html hippohtml="${requestScope.document.html}"/>
</div>

<%-- Generating a link to a binary asset resource. --%>
<c:if test="${not empty requestScope.document.resource}">
  <hst:link var="resource" hippobean="${requestScope.document.resource}" />
  <a href="${resource}">${requestScope.document.resource.name}</a>
</c:if>

<%-- Generating a link to a binary image resource. --%>
<c:if test="${not empty requestScope.document.image}">
  <img src="<hst:link hippobean="${requestScope.document.image.original}"/>"/>
</c:if>

Therefore you can easily leverage HST Link Rewriting features with those tags just like you have done in typical HST Component based applications.

Integration Examples with other Web Application Frameworks than Spring Web MVC

This Integration feature for External Web Application Frameworks is a very generic solution. Basically it prepares all the necessary request context information with URL/Content mappings, Content Beans Access API and Link Rewriting components before executing the external web application through the FilterChain. That's all.

Therefore it is theoretically possible to integrate almost any web application frameworks seamlessly if the framework is based on the JEE standard Filter/Servlet based application framework.

Currently we only provide a Spring Web MVC Framework integration example in the Hippo Testsuite project, but it's more than welcome if you want to try with other web application frameworks (such as Spring WebFlow, Tiles2, Struts2, Sitemesh, Wicket, Cocoon, etc.) and share your experiences with us. :-)