Tuesday, 7 January 2020

Converting Java DXA Webapp to Spring Boot APP

In this article, I would like to present the steps for converting the Java DXA (v2.2.1) application into a Spring Boot application.

Executable Artifact


As stated in Spring Boot’s documentation (https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-jsp-limitations), when running a Spring Boot application that uses an embedded servlet container (and is packaged as an executable archive), there are some limitations in the JSP support. The recommended approach is to build the solution as an executable WAR file, which can be launched with java -jar just like a normal jar executable.

Spring Boot dependencies


I have utilized an alternative to the starter spring-boot-starter-parent project since many implementations have a custom Maven parent. It is still possible to benefit from the spring-boot-starter-parent project's dependency tree by adding a dependency spring-boot-dependencies into our project in import scope.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-dependencies</artifactId>
  <version>1.5.21.RELEASE</version>
  <type>pom</type>
  <scope>import</scope>
</dependency>

The chosen Spring Boot version (1.5.21.RELEASE) automatically provides all the Spring dependencies in the version (v4.3.24.RELEASE) required by the DXA 2.2.1 framework. This way no other Spring Framework dependencies are needed in the Maven pom file.

The other required Spring Boot dependencies are:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <version>1.5.21.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-tomcat</artifactId>
  <version>1.5.21.RELEASE</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.apache.tomcat.embed</groupId>
  <artifactId>tomcat-embed-jasper</artifactId>
  <version>8.5.40</version>
  <scope>provided</scope>
</dependency>

The spring-boot-starter-tomcat and tomcat-embed-jasper dependencies are added to enable running the built executable WAR file inside an embedded Tomcat application server.

The spring-boot-maven-plugin is utilized to repackage the standard WAR build into an executable WAR file.

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <version>1.5.21.RELEASE</version>
  <configuration>
    <executable>true</executable>
  </configuration>
  <executions>
    <execution>
      <goals>
        <goal>repackage</goal>
      </goals>
      <configuration>
        <classifier>spring-boot</classifier>
        <mainClass>com.sdl.webapp.main.WebAppInitializer</mainClass>
      </configuration>
    </execution>
  </executions>
</plugin>

A full pom example can be viewed here.


Initializing the Application


The WebAppInitializer class is the starting point of the Spring Boot application.

package com.sdl.webapp.main;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class WebAppInitializer {

  public static void main(String[] args) {
    SpringApplication.run(WebAppInitializer.class, args);
  }
}


This class is annotated with @SpringBootApplication, which among other things, tells Spring to look for other components, configurations and services inside the same package. Therefore, I placed the SpringInitializer class inside the same package annotated with @Import(DxaSpringInitialization.class) in order to kick off the DXA initialization process.

package com.sdl.webapp.main;

import com.sdl.dxa.DxaSpringInitialization;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Import(DxaSpringInitialization.class)
@Configuration
public class SpringInitializer {
}



Application Properties


The spring.profiles.active property had to be moved from the dxa.properties file into Spring Boot's application.properties file.

Here are the contents of the application.properties file.

logging.config=classpath:logback.xml
spring.profiles.active=cil.providers.active,search.solr



Deploying the Application


The built executable WAR file is deployed from the command-line using:

java -jar dxaSpringBoot-2.2.1-spring-boot.war

The DXA application is started inside an embedded Tomcat application server.


Resources


For completeness, the supporting code and property extracts can be found here.


logging.config=classpath:logback.xml
# Multiple-line configuration is possible
# search.solr - Activates SOLR in Search, Not compatible with search.aws
# dynamic.navigation.provider - Dynamic navigation provider to be used instead of static
# adf.context.provider - Activates ADF instead of ContextService
spring.profiles.active=cil.providers.active,search.solr
#@formatter:off
## You can find the complete list of properties on DXA documentation page.
## This file is an example of how to override DXA properties and configure your DXA application.
dxa.caching.required.caches=defaultCache, pageModels, entityModels, failures
# Disabling ADF results in a significant performance gain,
# but ADF is needed for XPM Session Preview, Experience Optimization and Context Expressions.
dxa.web.adf.enabled=false
dxa.csrf.allowed=true
### ===================================================================================================================
### Model Service client configuration
### ===================================================================================================================
# By default DXA gets the URL of Model Service through Discovery Service. If you are not happy with this for any reason,
# you can specify the URL to your Model Service, which will be used instead.
dxa.model.service.url=http://model-service-url:9082
# Model Service doesn't have its own CIS capability and registers as a part of Content Service. This property sets a key name for the URL in extension properties of CS.
dxa.model.service.key=dxa-model-service
# These four properties set a default mapping of MS REST endpoints. Unless you really know what you're doing, don't change them.
dxa.model.service.url.entity.model=/EntityModel/{uriType}/{localizationId}/{componentId}-{templateId}
dxa.model.service.url.page.model=/PageModel/{uriType}/{localizationId}/{pageUrl}?includes={pageInclusion}
dxa.model.service.url.api.navigation=/api/navigation/{localizationId}
dxa.model.service.url.api.navigation.subtree=/api/navigation/{localizationId}/subtree/{siteMapId}?includeAncestors={includeAncestors}&descendantLevels={descendantLevels}
#@formatter:on
view raw dxa.properties hosted with ❤ by GitHub
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<version>2.2.1</version>
<artifactId>dxaSpringBoot</artifactId>
<packaging>war</packaging>
<name>Spring Boot DXA</name>
<properties>
<java-version>1.8</java-version>
<dxa-bom.version>2.2.1</dxa-bom.version>
<dxaversion>2.2.1</dxaversion>
<dxa-release-branch>release/2.2</dxa-release-branch>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.sdl.delivery</groupId>
<artifactId>udp-iq-api-common</artifactId>
<version>11.0.0-1049</version>
</dependency>
<dependency>
<groupId>com.sdl.delivery</groupId>
<artifactId>udp-core</artifactId>
<version>11.0.0-1044</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.21.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Dependencies -->
<dependency>
<groupId>com.sdl.dxa</groupId>
<artifactId>dxa-common-api</artifactId>
<version>${dxaversion}</version>
</dependency>
<dependency>
<groupId>com.sdl.dxa</groupId>
<artifactId>dxa-common</artifactId>
<version>${dxaversion}</version>
</dependency>
<dependency>
<groupId>com.sdl.dxa</groupId>
<artifactId>dxa-tridion-provider</artifactId>
<version>${dxaversion}</version>
</dependency>
<dependency>
<groupId>com.sdl.dxa.modules</groupId>
<artifactId>dxa-module-core</artifactId>
<version>${dxaversion}</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20140107</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.9</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.28</version>
</dependency>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.21.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>1.5.21.RELEASE</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>8.5.40</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.5.21.RELEASE</version>
<configuration>
<executable>true</executable>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<classifier>spring-boot</classifier>
<mainClass>com.sdl.webapp.main.WebAppInitializer</mainClass>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
view raw pom.xml hosted with ❤ by GitHub
package com.sdl.webapp.main;
import com.sdl.dxa.DxaSpringInitialization;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Import(DxaSpringInitialization.class)
@Configuration
public class SpringInitializer {
}
package com.sdl.webapp.main;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class WebAppInitializer {
public static void main(String[] args) {
SpringApplication.run(WebAppInitializer.class, args);
}
}

No comments:

Post a Comment