Skip to main content

Creating a DXA Java Module (part 2)

In my previous post Creating a DXA Java Module, I started presenting the steps for creating the Tridion items on the Content Manager needed for the new DXA module. In this current post, I present the Java code and configuration for having the DXA module run in a web-application.

I wrote my code as a separate JAR using IntelliJ and then running the DXA v1.5 web-application on Tomcat as part of the standard DXA reference implementation project dxa-web-application-java (available in GIT at https://github.com/sdl/dxa-web-application-java/tree/release/1.5).

What follows are the steps on how to create the DXA Java module classes and configurations.

1. Create the IntelliJ Emerald Module

In IntelliJ, create a new Java Module and enable Maven for it. Add the following dependencies in its pom.xml file:

    <dependencies>
        <dependency>
            <groupId>com.sdl.dxa</groupId>
            <artifactId>dxa-common-api</artifactId>
        </dependency>

        <!-- Servlet API -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>

        <!-- Spring Framework -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
        </dependency>
    </dependencies>

The final layout of the DXA module looks like the following:


2. Module Initializer Class

Configuration class EmeraldInitializer defines the mapping between ViewModels and class or domain models.

It extends AbstractInitializer, so it defines the getAreaName method, which returns the name of this module (i.e. Emerald).

Last, but not least it triggers the registration of the module's entities with the SemanticRegistryMapper, by defining method registerModels as a Spring post construct initialization method. Without this feature, only the entity models defined in the view-domain mappings are going to be registered with the Semantic Mapper. This has as undesired effect, the loss of embedded entities being added to the Semantic Mapper registries. As such, there will be no values for the embedded fields once the model is built.

package com.mihaiconsulting.dxa.emerald;

import com.mihaiconsulting.dxa.emerald.model.entity.MainNavigation;
import com.sdl.webapp.common.api.mapping.semantic.SemanticMappingRegistry;
import com.sdl.webapp.common.api.mapping.views.AbstractInitializer;
import com.sdl.webapp.common.api.mapping.views.ModuleInfo;
import com.sdl.webapp.common.api.mapping.views.RegisteredViewModel;
import com.sdl.webapp.common.api.mapping.views.RegisteredViewModels;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@org.springframework.context.annotation.Configuration
public class EmeraldInitializer {

    @RegisteredViewModels({
            @RegisteredViewModel(viewName = "MainNavigation", modelClass = MainNavigation.class)
    })
    @Component
    @ModuleInfo(name = "Emerald module", areaName = "Emerald")
    public static class EmeraldViewInitializer extends AbstractInitializer {

        @Autowired
        private SemanticMappingRegistry semanticMappingRegistry;

        @Override
        protected String getAreaName() {
            return "Emerald";
        }

        @PostConstruct
        private void registerModels() {
            semanticMappingRegistry.registerEntities(getClass().getPackage().getName());
        }
    }
}

Notice the mapping between view MainNavigation and model class MainNavigation.java. I will present those classes below.

3. The Models

Emerald module defines only two simple model classes MainNavigation and NavigationLinks. These are simple Java beans, which need to contain annotations that help the Semantic Mapper fill their properties with values from generic DD4T models.

The MainNavigation class is annotated with entityName MainNavigation which is in fact the Schema Root Element Name that maps to this model class. It also defines the vocabulary to use while mapping; this is the Schema target namespace defined in Tridion Content Manager, and the standard vocabulary prefix tri.

The MainNavigation.class is also used in the mapping in EmeraldInitializer between the view model and the class model that serves it.

Next, the class extends AbstractEntityModel class which helps identifying it as a model entity and provides additional metadata for XPM and other ids.

Finally, each property is annotated with mapping information from the Schema field using the vocabulary prefix and field XML name.

package com.mihaiconsulting.dxa.emerald.model.entity;

import com.sdl.webapp.common.api.mapping.semantic.annotations.SemanticEntity;
import com.sdl.webapp.common.api.mapping.semantic.annotations.SemanticProperty;
import com.sdl.webapp.common.api.model.entity.AbstractEntityModel;

import java.util.List;

@SemanticEntity(entityName = "MainNavigation", vocabulary = "http://www.sdl.com/web/schemas/core", prefix = "tri")
public class MainNavigation extends AbstractEntityModel {

    @SemanticProperty("tri:name")
    private String name;

    @SemanticProperty("tri:links")
    private List<NavigationLinks> links;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<NavigationLinks> getLinks() {
        return links;
    }

    public void setLinks(List<NavigationLinks> links) {
        this.links = links;
    }
}

The embedded entity NavigationLinks is presented below. Notice that it is an entity on its own that also extends AbstractEntityModel.

package com.mihaiconsulting.dxa.emerald.model.entity;

import com.sdl.webapp.common.api.mapping.semantic.annotations.SemanticEntity;
import com.sdl.webapp.common.api.mapping.semantic.annotations.SemanticProperty;
import com.sdl.webapp.common.api.model.entity.AbstractEntityModel;

@SemanticEntity(entityName = "NavigationLinks", vocabulary = "http://www.sdl.com/web/schemas/core", prefix = "tri")
public class NavigationLinks extends AbstractEntityModel {

    @SemanticProperty("tri:url")
    private String url;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }
}

4. JSP View Model

The ViewModel is called MainNavigation as per the mapping present in EmeralInitializer class when registering the view with the domain model. The view name is also referenced from the Component Template metadata defined in Tridion. The Metadata Schema field view defines the DXA module name and JSP view name in the format ModuleName:ViewName.

DXA uses a naming convention referring to the location of JSP views. They must be under path WEB-INF / Views / DXA-Module-Name / Entity. This makes the path of our view /WEB-INF/Views/Emerald/Entity.

Also, since this IntelliJ Java module is packaged as a JAR, the JSP views must be available in the JAR's classpath as resources. As such, we must put the JSP in the JAR under path /META-INF/WEB-INF/Views/Emerald/Entity/MainNavigation.jsp.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<jsp:useBean id="entity" type="com.mihaiconsulting.dxa.emerald.model.entity.MainNavigation" scope="request"/>

<div>
    <p>Name: ${entity.name}</p>

    <c:if test="${not empty entity.links}">
        Links:
        <ul>
            <c:forEach var="link" items="${entity.links}">
                <li>${link.url}</li>
            </c:forEach>
        </ul>
    </c:if>
</div>

The JSP/JSTL code in the view is trivial. It simply outputs the fields of the MainNavigation model and its multi-valued links. Notice how the entity bean is read from the request.

5. Spring Configuration

One additional configuration is required that indicates Spring framework should process the new DXA module for Spring annotations. We must tell Spring to scan for components under a certain package. In this case, we tell Spring to scan package com.mihaiconsulting.dxa.emerald and process the beans, controllers, auto-wired properties, post construct initialization methods.

DXA imports all /META-INF/spring-context.xml files it finds in all classpaths. As such, I defined one such file and placed it in the JAR's /META-INF/ folder.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.mihaiconsulting.dxa.emerald" />

</beans>

6. Run the Web-Application

Having all these configurations and classes, we can now run the web-application. Everything should be in place now to retrieve and build the models, then to render them in the JSP view:




Comments

Popular posts from this blog

Running sp_updatestats on AWS RDS database

Part of the maintenance tasks that I perform on a MSSQL Content Manager database is to run stored procedure sp_updatestats . exec sp_updatestats However, that is not supported on an AWS RDS instance. The error message below indicates that only the sa  account can perform this: Msg 15247 , Level 16 , State 1 , Procedure sp_updatestats, Line 15 [Batch Start Line 0 ] User does not have permission to perform this action. Instead there are several posts that suggest using UPDATE STATISTICS instead: https://dba.stackexchange.com/questions/145982/sp-updatestats-vs-update-statistics I stumbled upon the following post from 2008 (!!!), https://social.msdn.microsoft.com/Forums/sqlserver/en-US/186e3db0-fe37-4c31-b017-8e7c24d19697/spupdatestats-fails-to-run-with-permission-error-under-dbopriveleged-user , which describes a way to wrap the call to sp_updatestats and execute it under a different user: create procedure dbo.sp_updstats with execute as 'dbo' as...

I Have Gone Dark

Maybe it's the Holidays, but my mood has gone pretty dark. That is, regarding the look and feel of my computer and Tridion CME, of course. What I did was to dim the lights on the operating system, so I installed Placebo themes for Windows 7 . I went for the Ashtray look -- great name :) My VM looks now like this: But, once you change the theme on Windows, you should 'match' the theme of your applications. Some skin easily, some not. The Office suite has an in-built scheme, which can be set to Black , but it doesn't actually dim the ribbon tool bars -- it looks quite weird. Yahoo Messenger is skinnable, but you can't change the big white panels where you actually 'chat'. Skype is not skinnable at all. For Chrome, there are plenty of grey themes. Now i'm using Pro Grey . But then I got into changing the theme of websites. While very few offer skinnable interfaces (as GMail does), I had to find a way to darken the websites... Enter Stylish -- a pl...

REL Standard Tag Library

The RSTL is a library of REL tags providing standard functionality such as iterating collections, conditionals, imports, assignments, XML XSLT transformations, formatting dates, etc. RSTL distributable is available on my Google Code page under  REL Standard Tag Library . Always use the latest JAR . This post describes each RSTL tag in the library explaining its functionality, attributes and providing examples. For understanding the way expressions are evaluated, please read my post about the  Expression Language used by REL Standard Tag Library . <c:choose> / <c:when> / <c:otherwise> Syntax:     <c:choose>         <c:when test="expr1">             Do something         </c:when>         <c:when test="expr2">             Do something else         </c:when...