earldouglas / sbt-war

Package and run .war files with sbt
BSD 3-Clause "New" or "Revised" License
382 stars 105 forks source link

Add support for running with Undertow #431

Closed earldouglas closed 1 month ago

earldouglas commented 3 years ago

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

AugustNagro commented 3 years ago

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.

earldouglas commented 3 years ago

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

earldouglas commented 3 years ago

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!
AugustNagro commented 3 years ago

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"]
earldouglas commented 3 years ago

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
AugustNagro commented 3 years ago

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 commented 3 years ago

Ok, got it working in JBoss!

Just switched to the ContainerPlugin.

earldouglas commented 3 years ago

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.

AugustNagro commented 3 years ago

Sweet, that's a better solution.