How to include dynamic content in a DD4T Spring MVC app

A typical DD4T page simply displays the component presentations on that page. But what if your requirements go a little further than that? What if you are asked to show a list of all press releases in the system, and you want to retrieve them from SDL’s broker database? This article explains how you would set about to achieve this.

When you run a DD4T-driven Spring MVC web application, you normally have at least 2 controllers:

  • The PageController which serves all regular pages
  • The ComponentPresentationController which serves the component presentations (both the dynamic ones and the ones that are embedded on a page)

The PageController has a showPage action method that is normally mapped to all requests that represent a page in Tridion. For example: it can be mapped to all URLs ending with ‘.html’. Like this:

@RequestMapping(value = {"/**/*.html" }, method = {RequestMethod.GET})
 @Override public String showPage (final Model model, final HttpServletRequest request, final HttpServletResponse response) throws IOException {
    // ...
}

The ComponentPresentationController has an action method called ‘showComponentPresentation’. This is mapped as follows:

@RequestMapping (value = {"/{componentViewName}/{componentId}.dcp"}, method = {RequestMethod.GET, RequestMethod.HEAD})
@Overrid public String showComponentPresentation (@PathVariable final String componentViewName, @PathVariable final int componentId, final HttpServletRequest request) {
    // ...
}

How does this work, you may wonder? Well, the page view contains a <dd4t:componentPresentations/> tag, which renders the component presentations. This tag dispatches a request the consists of the component view name and the component id, for example:

/pressrelease/8-4465.dcp

The showComponentPresentation method reads the component presentation from the request and renders it with the specified view. The output of this view is included in the output of the page view, in the location of the <dd4t:componentPresentations/> tag.

That’s how it works normally. A page with 2 component presentations might (schematically) look like this:

The grey box represents the HTML that is generated by the page view, the blue boxes are generated by the component views (through the ComponentPresentationController).

Hijacking the component presentation

One of the advantages of this approach, is that it allows the application developer to ‘hijack’ a component presentation. Let’s imagine my page contains the following component presentations:

  1. A Header component (based on the schema Header) coupled with a ‘Hero image header’ component template
  2. A Press Release Overview component (based on the schema Overview) coupled with a ‘Press Release Overview’ component template

The plan is that the header is simply static content, while the press release overview shows all the press releases that are currently published to the broker database. In the diagram below, the blue box is the simple, static header component, while the yellow box represents the list of all press releases in the broker:

To make this happen, we need to hijack the request to the second component presentation so we can add our own code. How? Simple!

First, we need to create our own controller. This can be a regular Spring controller, or we can extend the AbstractComponentPresentationController that is part of the DD4T mvc-support package. In this article I will show you the latter.

 

package com.acme.web;
public class PressReleaseController extends AbstractComponentPresentationController {
   @Resource
   private ComponentPresentationFactoryImpl componentPresentationFactory;
}

Note that a “componentPresentationFactory” is automatically injected by Spring. We will need this later.

Next, we will add an action method like this:

@RequestMapping(value = { "/pressreleaseoverview/{componentId}.dcp" }, method = { RequestMethod.GET,
 RequestMethod.HEAD })
 public String pressReleaseOverview(@PathVariable final int componentId, final HttpServletRequest request)
 throws StorageException {

Notice the requestMapping. It has the name of our pressreleaseoverview view in it. Next time the page view attempts to render a component presentation which uses this view, there are 2 suitable request mappings in my application:

  • /pressreleaseoverview/{componentId}.dcp” }
  • /{componentViewName}/{componentId}.dcp

Since both mappings are a match, which one will be activated? Spring MVC handles this dilemma intelligently: the most specific mapping “wins”. In other words: the mapping with the least number of ‘curly braces’ in it. In this case, it is the mapping of our pressReleaseOverview action that wins the deal.

This is what I mean by ‘hijacking’ the component presentation.

Querying for press releases in the broker

The last part of the solution is actually the easiest. In our action method, we can use the Tridion CD API to look for all components based on the schema ‘Press Release’. For example:

 Criteria usesSchema = new SchemaTitleCriteria("Press Release");
 Query query = new Query(usesSchema);
 String[] results = query.executeQuery();

The String array ‘results’ contains the TCM uris of all matching components.

Next, we will use the component presentation factory to retrieve the dynamic component presentations from the broker:

 

List<PressRelease> pressReleases = new LinkedList<PressRelease>();
try {

    for (int i = 0; i < results.length; i++) {
        ComponentPresentation cp = componentPresentationFactory.getComponentPresentation(results[i]);
        Map<String, BaseViewModel> viewModels = cp.getAllViewModels();
        Iterator<?> it = viewModels.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry pair = (Map.Entry) it.next();
            BaseViewModel vm = (BaseViewModel) pair.getValue();
            if (vm.getClass() == PressRelease.class) {
                pressReleases.add((PressRelease)vm);
            }
        }
    }
} catch (FactoryException e) {
    // TODO: catch error
}

We now have a list with press releases. We need to pass this to the component view by adding it to the request, like this:

request.setAttribute("pressReleases", pressReleases);

Lastly, we will hand over to the view by simply calling the super method:

return super.showComponentPresentation("pressreleaseoverview", componentId, request);

There is one  more thing to do: implement our view. For this purpose, we create a jsp file called pressreleaseoverview.jsp. It could look like this:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<jsp:useBean id="results" type="java.lang.String[]" scope="request"/>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>


<h1>All press releases</h1>

<c:forEach var="pressRelease" items="${pressReleases}">
 <h3>${pressRelease.heading}</h3>
 ${pressRelease.summary}
 <br/>
 <a href="${pressRelease.linkToRelease}">Go to release</a> 
</c:forEach>

That’s it. We have now succeeded in hijacking the ‘press release overview’ component presentation, and using it to display a list of press releases.

Of course, press releases are just an example. You can use this approach to add just about any kind of content to your web pages. You could even access some external data source and display third party content in your site.

Happy coding!