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.
The final layout of the DXA module looks like the following:
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.
Notice the mapping between view MainNavigation and model class MainNavigation.java. I will present those classes below.
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.
The embedded entity NavigationLinks is presented below. Notice that it is an entity on its own that also extends AbstractEntityModel.
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.
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.
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.
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>
Comments