Skip to main content

Use WAR Deployment with Spring Boot

This document outlines how to adjust a Spring Boot project to use WAR deployment (also known as traditional deployment). The traditional deployment of Spring helps overcome exceptions related to the SAP JCo library.

Modern and Traditional Spring Deployment

Traditionally, Java projects based on Spring were built as war files which had to be deployed to standalone application servers, such as Tomcat. With the rise of Spring Boot, the target file type changed to jar files where the application server is already built in. The advantage of that modern deployment form is that the application can be started directly without the need to administer a complex application server.

Target Projects of This Guide

This guide is applicable to all Spring-based Java projects which use the modern deployment of Spring as jar file. That comprises in the first place any project using the new CAP stack as well as the Spring Boot Maven archetype of the SAP Cloud SDK.

The problem with the modern deployment is that the SAP Java Connector library cannot be used when the application gets deployed as jar file. That results in runtime exceptions like java.lang.NoClassDefFoundError: com/sap/conn/jco/JCoException.

To overcome this problem, we'll outline how to adjust your project to leverage the traditional Spring deployment instead.

Use Traditional Spring Deployment

Perform the following steps to switch your project from using the modern to the traditional Spring deployment.

Change Maven Packaging

In your pom.xml, change the packaging setting from jar to war.

<!-- before: -->
<!-- <packaging>jar</packaging> -->

<!-- after: -->
<packaging>war</packaging>
Use the right pom.xml

When your project uses multiple Maven modules, you need to choose the pom.xml that contains your application code. For instance, in a CAP project use the pom.xml of the srv module.

Add Maven WAR Plugin

Add the Maven WAR plugin to the pom.xml:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<!-- use latest version here -->
<version>3.3.2</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>

Adjust Dependency Scopes

The WAR file will be deployed into an a Tomcat application container. Thus we no longer need to include the dependencies that are now provided by the container.

Adjust your dependency management section to contain the following entries:

pom.xml
<dependencyManagement>
<dependencies>
<!-- Cloud SDK + SAP Java Buildpack BOMs -->
<dependency>
<groupId>com.sap.cloud.sdk</groupId>
<artifactId>sdk-sjb-bom</artifactId>
<version>${cloudsdk.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Work around error in Buildpack BOM -->
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>stax2-api</artifactId>
<scope>compile</scope>
</dependency>
<!-- Set Spring Boot Tomcat dependencies to provided -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>

<!-- Any further dependency management you might use
e.g. org.springframework.boot:spring-boot-dependencies -->
</dependencies>
</dependencyManagement>
Ordering of dependencies

Please note that the ordering of entries under the <dependencyManagement> matters. To ensure the correct scopes are set make sure the order is implemented as shown above. In particular the sdk-sjb-bom should be the first entry if possible.

  • If you are already using the sdk-bom simply replace it with the sdk-sjb-bom
  • The versions for stax2-api and spring-boot-starter-tomcat are not relevant, only the scopes matter
Further Guidance

You can find more guidance on how to manage dependencies around the SAP Java Buildpack in our dedicated guide here.

Let Application Class Inherit from SpringBootServletInitializer

Make your application class a subclass of the class SpringBootServletInitializer.

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

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

Change the Deployment Configuration

Go to your deployment descriptor, for instance to your mta.yaml, and locate the module that contains your application under modules. For that particular module, perform the following changes:

  • Change the type from java to java.tomcat.
  • Change the build-result so that the file extension is war.
  • If not present, add the property USE_JCO with the value true under properties.
  • If not present, add buildpack: sap_java_buildpack under parameters.

In the following example we have commented the old values for your reference:

modules:
- name: example-project-srv
#type: java
type: java.tomcat
path: srv
properties:
SPRING_PROFILES_ACTIVE: cloud
# make sure that the property USE_JCO is present
USE_JCO: true
build-parameters:
builder: custom
commands:
- mvn clean package
# set the WAR file according to your artifact id
build-result: target/example-project.war # do not use the -exec.war variant
parameters:
buildpack: sap_java_buildpack
provides:
- name: srv-api
properties:
srv-url: ${default-url}
requires:
- name: my-destination

Build and Deploy the Project

The presented changes do not lead to any changes in the build and deployment process. Hence, you can build and deploy your Java project as usually.

From now on, the JCo classes should be found during runtime.