Kenco
Kenco

Reputation: 703

Can't Deploy Spring Boot EAR in JBoss 6.4 EAP

Edit

Alright, so it looks like the SpringBootServletInitializer isn't detected because it's inside a JAR in the EAR, not the WAR. What I did was make a new module and included it in my WAR. This contained a META-INF directory with a services folder. That services folder has a single file (javax.servlet.ServletContainerInitializer) with the contents org.springframework.web.SpringServletContainerInitializer. This then tries to deploy the WAR, but fails with the following:

Caused by: org.jboss.as.server.deployment.DeploymentUnitProcessingException: JBAS018104: Deployment error processing SCI for jar: test-1.0.0-SNAPSHOT.jar
        at org.jboss.as.web.deployment.ServletContainerInitializerDeploymentProcessor.loadSci(ServletContainerInitializerDeploymentProcessor.java:202)
        ... 7 more
Caused by: java.lang.ClassCastException: org.springframework.web.SpringServletContainerInitializer cannot be cast to javax.servlet.ServletContainerInitializer
        at org.jboss.as.web.deployment.ServletContainerInitializerDeploymentProcessor.loadSci(ServletContainerInitializerDeploymentProcessor.java:194)
        ... 7 more

Again, it looks like the Servlet API is conflicting with another - but there's only the one on the classpath from what I can tell (and I even scoped it as provided for this test).

Original

Currently I'm having a few issues deploying a Spring-Boot (1.3.2.RELEASE) EAR in JBoss 6.4 EAP. The EAR file simply wraps a skinny WAR and all the JARs in a /lib folder.

I had previously made the changes required to deploy the WAR file, but we've unfortunately run into the requirement that the deployment has to be an EAR.

Deploying as a WAR

To deploy the application as a WAR, I changed the following:

pom.xml

    <!-- JavaEE 7 for JPA 2.1 -->
    <dependency>
        <groupId>javax</groupId>
        <artifactId>javaee-api</artifactId>
        <version>7.0</version>
    </dependency>

    <!-- Exclude Tomcat -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <!-- Include Servlet 3 -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.0.1</version>
    </dependency>

jboss-deployment-structure.xml

<?xml version="1.0"?>
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
    <deployment>
        <exclude-subsystems>
            <subsystem name="jpa"/>
        </exclude-subsystems>
        <exclusions>
            <module name="javaee.api"/>
        </exclusions>
    </deployment>
</jboss-deployment-structure>

Application.java

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

    private static final Class<Application> APPLICATION_CLASS = Application.class;

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

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(APPLICATION_CLASS);
    }

}

These changes worked fine, and I could now deploy the WAR to JBoss 6.4.

Deploying as an EAR

This is where the trouble starts. I created a new module to build the EAR file using the maven-ear-plugin:

<build>
    <finalName>my-application</finalName>
    <plugins>
        <plugin>
            <artifactId>maven-ear-plugin</artifactId>
            <version>2.10.1</version>
            <configuration>
                <version>6</version>
                <skinnyWars>true</skinnyWars>
                <defaultLibBundleDir>lib</defaultLibBundleDir>
                <fileNameMapping>no-version</fileNameMapping>
                <modules>
                    <webModule>
                        <groupId>com.test.app</groupId>
                        <artifactId>my-application</artifactId>
                        <bundleFileName>my-application.war</bundleFileName>
                        <context-root>/my-app</context-root>
                    </webModule>
                </modules>
            </configuration>
        </plugin>
    </plugins>
</build>

I also added the jboss-deployment-structure.xml under /src/main/application/META-INF, so that it would be included in the EAR (it needs to be at the highest level). Trying to deploy this results in "success", but JBoss never tries to deploy the WAR inside the EAR. It seems that a web.xml is needed.

So looking at the docs, it seems that I need to define a web.xml like follows:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.test.app.Application</param-value>
    </context-param>

    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextAttribute</param-name>
            <param-value>org.springframework.web.context.WebApplicationContext.ROOT</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

Now there is some additional configuration if I want to drop to Servlet 2.5 - but I don't want to do this.

Attempting to deploy this results in the following exception:

    10:29:40,586 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/company-api-entity]] (ServerService Thread Pool -- 56) JBWEB000289: Servlet appServlet threw load() exception: java.lang.ClassCastException: org.springframework.web.servlet.DispatcherServlet cannot be cast to javax.servlet.Servlet
            at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1154) [jbossweb-7.5.7.Final-redhat-1.jar:7.5.7.Final-redhat-1]
            at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1100) [jbossweb-7.5.7.Final-redhat-1.jar:7.5.7.Final-redhat-1]
            at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:3593) [jbossweb-7.5.7.Final-redhat-1.jar:7.5.7.Final-redhat-1]
            at org.apache.catalina.core.StandardContext.start(StandardContext.java:3802) [jbossweb-7.5.7.Final-redhat-1.jar:7.5.7.Final-redhat-1]
            at org.jboss.as.web.deployment.WebDeploymentService.doStart(WebDeploymentService.java:163) [jboss-as-web-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
            at org.jboss.as.web.deployment.WebDeploymentService.access$000(WebDeploymentService.java:61) [jboss-as-web-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
            at org.jboss.as.web.deployment.WebDeploymentService$1.run(WebDeploymentService.java:96) [jboss-as-web-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
            at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [rt.jar:1.8.0_45]
            at java.util.concurrent.FutureTask.run(FutureTask.java:266) [rt.jar:1.8.0_45]
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [rt.jar:1.8.0_45]
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [rt.jar:1.8.0_45]
            at java.lang.Thread.run(Thread.java:745) [rt.jar:1.8.0_45]
            at org.jboss.threads.JBossThread.run(JBossThread.java:122)

This would seem that there's a mismatch in the servlet versions being loaded, but I can't seem to verify that it is (or exclude it). I've tried setting the scope to provided (so the JBoss version takes over), but this results in the following error:

11:18:47,141 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/company-api-entity]] (ServerService Thread Pool -- 52) JBWEB000289: Servlet appServlet threw load() exception: java.util.MissingResourceException: Can't find bundle for base name javax.servlet.LocalStrings, locale en_IE
        at java.util.ResourceBundle.throwMissingResourceException(ResourceBundle.java:1564) [rt.jar:1.8.0_45]
        at java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:1387) [rt.jar:1.8.0_45]
        at java.util.ResourceBundle.getBundle(ResourceBundle.java:773) [rt.jar:1.8.0_45]
        at javax.servlet.GenericServlet.<clinit>(GenericServlet.java:95) [jboss-servlet-api_3.0_spec-1.0.2.Final-redhat-2.jar:1.0.2.Final-redhat-2]

Clearly it's using the JBoss 3.0 servlet spec, but it would seem the actual servlet 3.0 JAR is missing.

Is there something that I'm missing? Does the WAR actually need a web.xml, or is there some other way? Is what I'm trying even possible?

Any help would be greatly appreciated.

Upvotes: 4

Views: 4875

Answers (1)

Kenco
Kenco

Reputation: 703

Fixed the issue. Starting from the WAR deployment, I made an additional EAR module. I shouldn't have used a skinnyWar, and instead left all the JARs inside the WAR. This allows Spring to properly bootstrap the Spring Boot servlet.

This has the following configuration:

<build>
    <finalName>my-application</finalName>
    <plugins>
        <plugin>
            <artifactId>maven-ear-plugin</artifactId>
            <version>2.10.1</version>
            <configuration>
                <modules>
                    <webModule>
                        <groupId>com.test.app</groupId>
                        <artifactId>my-application</artifactId>
                        <bundleFileName>my-application.war</bundleFileName>
                        <context-root>/my-app</context-root>
                    </webModule>
                </modules>
            </configuration>
        </plugin>
    </plugins>
</build>

I also made sure that there were no compile scoped dependencies in my parent module (the root pom).

I then made sure to have the following file in my EAR:

src/main/application/META-INF/jboss-deployment-structure.xml

While this was in my WAR, the main problem I had was that I was still using <deployment> instead of <sub-deployment>

<?xml version="1.0"?>
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
    <sub-deployment name="my-application.war">
        <exclude-subsystems>
            <subsystem name="jpa"/>
        </exclude-subsystems>
        <exclusions>
            <module name="javaee.api"/>
        </exclusions>
    </sub-deployment>
</jboss-deployment-structure>

This properly excludes the javaee.api module and the jpa subsystem.

After this, the EAR deployed correctly to JBoss.

Upvotes: 1

Related Questions