xsbt-web-plugin icon indicating copy to clipboard operation
xsbt-web-plugin copied to clipboard

Add support for running with Undertow

Open earldouglas opened this issue 4 years ago • 9 comments

https://undertow.io/undertow-docs/undertow-docs-2.0.0/#creating-a-servlet-deployment

earldouglas avatar Feb 15 '21 13:02 earldouglas

I'm trying to build a Spring Boot WAR for JBoss via the ContainerPlugin, without luck.

Is it not working because xsbt-web-plugin doesn't support Undertow yet? It looks like tomcat-embed-*.jar is added to the manifest regardless of what I put in Container / containerLibs.

AugustNagro avatar Jun 20 '21 07:06 AugustNagro

Thanks for the info; that helps point me in the right direction. I will put something together for this later today.

earldouglas avatar Jun 20 '21 12:06 earldouglas

Can you give TomcatPlugin a try, rather than ContainerPlugin? I can get a minimal Spring Boot project working with it:

  1. Follow steps (1) and (2) on the Spring Quickstart Guide

    Step 1: Start a new Spring Boot project Step 2: Add your code

  2. Add project/plugins.sbt
    addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.2")
    
  3. Port the given mvn.xml to build.sbt and enable TomcatPlugin
    libraryDependencies ++=
      Seq(
        "org.springframework.boot" % "spring-boot-starter-web" % "2.5.1",
        "org.springframework.boot" % "spring-boot-starter-tomcat" % "2.5.1" % "provided",
        "org.springframework.boot" % "spring-boot-starter-test" % "2.5.1" % "test",
      )
    enablePlugins(TomcatPlugin)
    
  4. Run it
    $ sbt
    > tomcat:start
    
  5. Test it
    $ curl localhost:8080/hello
    Hello World!
    

earldouglas avatar Jun 20 '21 20:06 earldouglas

Wow, thanks for the fast reply!

I followed your steps and created a minimal reproducer here: https://github.com/AugustNagro/springboot-mvp

Running with run works, but Tomcat / start does not. The log is:

INFO: Initializing ProtocolHandler ["http-nio-8080"]
Jun 20, 2021 7:24:40 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service [Tomcat]
Jun 20, 2021 7:24:40 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet engine: [Apache Tomcat/9.0.38]
Jun 20, 2021 7:24:40 PM org.apache.catalina.startup.ContextConfig getDefaultWebXmlFragment
INFO: No global web.xml found
Jun 20, 2021 7:24:42 PM org.apache.jasper.servlet.TldScanner scanJars
INFO: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
Jun 20, 2021 7:24:42 PM org.apache.catalina.core.ApplicationContext log
INFO: 1 Spring WebApplicationInitializers detected on classpath
Jun 20, 2021 7:24:42 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-nio-8080"]

AugustNagro avatar Jun 21 '21 02:06 AugustNagro

I don't know much about Spring Boot, but based on the example project from https://start.spring.io/, I think you need to add one additional class:

src/main/scala/com/example/mvp/ServletInitializer.scala:

package com.example.mvp

import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer

class ServletInitializer extends SpringBootServletInitializer:

  override def configure(application: SpringApplicationBuilder): SpringApplicationBuilder =
    application.sources(classOf[SpringMVP])

Then run tomcat:start, and you should see a bunch of Spring-related output, as well as a working endpoint:

$ curl localhost:8080
Gus

earldouglas avatar Jun 21 '21 02:06 earldouglas

Awesome, tomcat:start is working!

But when running on JBoss, I get

java.lang.ClassCastException: org.apache.tomcat.websocket.server.WsServerContainer cannot be cast to io.undertow.websockets.jsr.ServerWebSocketContainer​

I've tried excluding the Tomcat WS dependency as per stack overflow, but no dice so far.

    excludeDependencies ++= Seq(
      // https://stackoverflow.com/questions/25792121/spring-boot-websockets-in-wildfly?answertab=votes#tab-top
      "org.apache.tomcat.embed" % "tomcat-embed-websocket"
    )

AugustNagro avatar Jun 21 '21 03:06 AugustNagro

Ok, got it working in JBoss!

Just switched to the ContainerPlugin.

AugustNagro avatar Jun 21 '21 03:06 AugustNagro

It looks like spring-boot-starter-web is pulling in spring-boot-starter-tomcat as a transitive dependency, which conflicts with JBoss's own libraries. You can exclude it from being included by adding an exclude() to the libraryDependencies line:

libraryDependencies ++= Seq(
  "org.springframework.boot" % "spring-boot-starter-web" % SpringBootVersion exclude("org.springframework.boot", "spring-boot-starter-tomcat"),

Just switched to the ContainerPlugin.

Note that this has no effect on the .war file produced by package, and will prevent you from being able to launch Tomcat via tomcat:start.

earldouglas avatar Jun 21 '21 03:06 earldouglas

Sweet, that's a better solution.

AugustNagro avatar Jun 21 '21 05:06 AugustNagro