3

I have packaged my spring boot application using sbt assembly and when trying to run the jar I receive

Application startup failed
org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is 
    org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.

The project works in Intellij just fine. My build.sbt is below

lazy val news = (project in file("wherever")).
enablePlugins(DockerPlugin).
settings(commonSettings: _*).
settings(
  name := "name",
  mainClass in assembly := Some("mainEntry"),
  test in assembly := {},
  assemblyJarName := "jarName",
  libraryDependencies ++= dependency1,
  libraryDependencies ++= dependency2,
  libraryDependencies += ScalaTest,
  scalaSource in Compile := baseDirectory.value / "src/main/scala",
  dockerfile in docker := {
    val artifact: File = assembly.value
    val artifactTargetPath = "/"
    new Dockerfile {
      from("openjdk:8-jre-alpine")
      add(artifact, artifactTargetPath)
      add(new File("./config/"),"/config")
      cmd("java", "-jar", " -Denv=$env","jarName")
      env("env","stage")
    }
  },
  imageNames in docker := Seq(
    ImageName("imageName")
  )
)

I've done some digging around and it looks like Spring Boot requires the jar to contain nested jars (instead of an Uber jar like is created with sbt assembly). So, that gives me two options. Package my jar up as a nested jar with sbt or configure spring to use a normal classloader and load from an Uber jar.

I've looked into nested jar sbt plugins and I cannot seem to find anything which is maintained (or even in maven central). This and this are both years out of date and not in maven central. Does anyone have any suggestions for doing this?

I've also looked into configuring spring boot to use uber jars and the overwhelming response is just "use the maven plugin" which is not applicable here.

cscan
  • 3,684
  • 9
  • 45
  • 83

1 Answers1

0

You need to provide an embedded servlet container factory on the application. It should look something like the following:

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory
import org.springframework.boot.context.web.SpringBootServletInitializer
import org.springframework.context.annotation.{Bean, ComponentScan, Configuration, PropertySource}

/**
 * Spring boot application configuration and servlet initializer.
 */
@Configuration
@ComponentScan(value = Array("com.foobusiness.foopackage"))
@PropertySource(Array("classpath:application.properties"))
@SpringBootApplication(exclude = Array(classOf[ErrorMvcAutoConfiguration]))
class Application extends SpringBootServletInitializer {
  @Bean
  def servletContainer: JettyEmbeddedServletContainerFactory = new JettyEmbeddedServletContainerFactory()
}

object Application {
  def main(args: Array[String]): Unit = {
    SpringApplication run classOf[Application]
  }
}

The above uses Jetty, obviously, which means you'd also need to include the jetty-runner as a library compile-time dependency. Your sbt build file also needs to use the above class when compiling, running or packaging:

mainClass in(Compile, run, packageBin) := Some("com.foobusiness.foopackage.Application"),

The application can be run from command-line with sbt run

kflorence
  • 2,187
  • 2
  • 16
  • 15