Spring Boot

Sections in this Guide

  1. MDW as a Spring Boot Dependency
  2. Spring Assets
  3. Project Build Considerations
  4. Runtime Issues

1. MDW as a Spring Boot Dependency

Building MDW into your Spring Boot project is as simple as adding the mdw-spring-boot dependency. You’ll automagically get the MDWHub webapp and expose your MDW REST APIs.

1.1 Gradle

  dependencies {
      compile group: 'com.centurylink.mdw', name: 'mdw-spring-boot', version: mdwVersion
      compile group: 'org.springframework.boot', name: 'spring-boot-starter', version: springBootVersion
      
      // asset package dependencies
      compileOnly fileTree(dir: "${assetLoc}", includes: ["**/*.jar"])
  }  

Gradle is the preferred build automation tool for MDW.

1.2 Maven

  <dependencies>
    <dependency>
      <groupId>com.centurylink.mdw</groupId>
      <artifactId>mdw-spring-boot</artifactId>
      <version>${mdw.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- kotlin asset package dependencies -->
    <dependency>
      <groupId>com.centurylink.mdw.kotlin</groupId>
      <artifactId>script-engine.jar</artifactId>
      <version>0.4</version>
      <scope>system</scope>
      <systemPath>${project.basedir}/assets/com/centurylink/mdw/kotlin/script-engine.jar</systemPath>
    </dependency>        
    <dependency>
      <groupId>com.centurylink.mdw.microservice</groupId>
      <artifactId>service-plan.jar</artifactId>
      <version>0.2</version>
      <scope>system</scope>
      <systemPath>${project.basedir}/assets/com/centurylink/mdw/microservice/service-plan.jar</systemPath>
    </dependency>        
  </dependencies>  

1.3 MDW Studio

With MDW Studio you can use the New Project wizard to create an MDW Spring Boot project with the appropriate build file dependencies: New Project MDW Section 1.2 of the User Guide walks you through how to do this.

1.4 MDW CLI

The MDW CLI comes with the ability to create a new project, which can be built and run from the command line, and/or imported into MDW Studio. Here’s the command to create a new Spring Boot project with default options:

  mdw init --spring-boot

Check out the Quick Start guide for a walk through describing this approach.

2. Spring Assets

2.1 Spring config assets

A Spring config asset is an XML file identified by the .spring extension. It contains standard Spring config content as in this datasource example:

  <?xml version="1.0" encoding="UTF-8"?>
  xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:ctx="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">
    <bean id="myDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
      <property name="driverClassName" value="#{T(com.centurylink.mdw.config.PropertyManager).getProperty('my.database.driver')}" />
      <property name="url" value="#{T(com.centurylink.mdw.config.PropertyManager).getProperty('my.db.url')}" />
      <property name="username" value="#{T(com.centurylink.mdw.config.PropertyManager).getProperty('my.db.username')}" />
      <property name="password" value="#{T(com.centurylink.mdw.config.PropertyManager).getProperty('my.db.password')}" />
      <property name="maxTotal" value="#{T(com.centurylink.mdw.config.PropertyManager).getProperty('my.database.poolsize')}" />
      <property name="maxIdle" value="#{T(com.centurylink.mdw.config.PropertyManager).getProperty('my.database.poolMaxIdle')}" />
      <property name="validationQuery" value="SELECT 1 FROM DUAL" />
      <property name="testOnBorrow" value="true" />
      <property name="testWhileIdle" value="true" />
      <property name="removeAbandonedOnBorrow" value="true" />
      <property name="logAbandoned" value="true" />
      <property name="removeAbandonedTimeout" value="#{T(com.centurylink.mdw.config.PropertyManager).getIntegerProperty('mdw.database.timeout',1000)}" />
    </bean>
  </bean>

2.2 Spring-annotated Java assets

Coming soon.

3. Project Build Considerations

3.1 Asset jar dependencies

In the example Gradle build script in section 1, notice the compileOnly fileTree dependency on jar files among your assets:

      compileOnly fileTree(dir: "${assetLoc}", includes: ["**/*.jar"])

This is to enable code completion and syntax highlighting in MDW Studio, which bases its dependency resolution on Gradle or Maven. The reason for compileOnly is so that these jars do not get bundled into your generated boot jar, which would defeat the purpose of treating them as dynamic assets.

3.2 Boot jar generation

To avoid runtime NoClassDefFoundErrors, it’s imperative that you customize the bootJar task as in the example:

  bootJar {
      // Exclude assets from packaging to avoid NoClassDefFoundErrors
      // (do not overlap packages between src/main/java and assets).
      def assetPackages = com.centurylink.mdw.file.Packages(file(assetLoc))
      exclude {
          assetPackages.isAssetOutput(it.relativePath.toString())
      }
  }

The purpose of this is to exclude all asset packages from your generated boot jar. Read on if you’re curious about why this is needed.

3.3 Runtime class loading

Aside from asset jars, you can of course also have regular old static dependencies that are built into your boot jar:

     compile 'com.google.code.gson:gson:2.8.5'

These dependencies are available to all your compilable assets (i.e. Java/Kotlin/Groovy) in the usual way.

You’ll probably also have source code under src/main/java (or elsewhere) that’s not an MDW asset but is leveraged by your dynamic Java/Kotlin assets. Compiled classes from src/main/java are packaged by the standard bootJar task into the BOOT-INF/classes directory of your boot jar, and are accessed from there by MDW assets that depend on them.

Every asset package in MDW gets its own ClassLoader that delivers asset classes that live within. Compilable asset dependencies are resolved like this:

  Asset Package ClassLoader > Other MDW Package ClassLoaders > Spring Boot ClassLoader
  

So if your compiled assets end up in the boot jar and are loaded by the Spring Boot ClassLoader, at runtime they won’t be able to resolve other compiled assets that they might depend on. The golden rule is to keep compiled assets out of your boot jar’s BOOT-INF/classes directory. The MDW Packages utility makes this easy, but it relies on an ironclad rule that there is no naming overlap between your src/main/java packages and MDW asset packages.

4. Runtime Issues

4.1 Excessive ActiveMQ logging

Depending on your dependencies, you may see repeated logging output like this:

  2019-07-14 21:02:44.865  INFO 78232 --- [http-nio-9081-exec-1] o.a.activemq.broker.TransportConnector   : Connector vm://localhost started
  2019-07-14 21:02:44.877  INFO 78232 --- [http-nio-9081-exec-1] o.a.activemq.broker.TransportConnector   : Connector vm://localhost stopped
  2019-07-14 21:02:47.004  WARN 78232 --- [http-nio-9081-exec-3] o.apache.activemq.broker.BrokerRegistry  : Broker localhost not started so using mdw-activemq instead

You can avoid this by adding the following to application.yml:

  spring:
    activemq:
      broker-url: vm://mdw-activemq