Arlo Guthrie
Arlo Guthrie

Reputation: 1234

Restful services with Spring Boot on Wildfly 8.x

I'm having issues getting a simple hello world application to work on Wildfly 8.x. The stack trace I receive is a null pointer exception getting the headers of the response:

2016-07-15 04:14:28,488 ERROR [org.springframework.boot.context.web.ErrorPageFilter] (default task-4) Forwarding to error page from request [/hello] due to exception [null]: java.lang.NullPointerException
    at java.util.ArrayList.<init>(ArrayList.java:177) [rt.jar:1.8.0_92]
    at io.undertow.servlet.spec.HttpServletResponseImpl.getHeaders(HttpServletResponseImpl.java:248) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at javax.servlet.http.HttpServletResponseWrapper.getHeaders(HttpServletResponseWrapper.java:303) [jboss-servlet-api_3.1_spec-1.0.0.Final.jar:1.0.0.Final]
    at org.springframework.http.server.ServletServerHttpResponse$ServletResponseHttpHeaders.get(ServletServerHttpResponse.java:160) [spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]
    at org.springframework.http.server.ServletServerHttpResponse$ServletResponseHttpHeaders.containsKey(ServletServerHttpResponse.java:142) [spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.addContentDispositionHeader(AbstractMessageConverterMethodProcessor.java:346) [spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:238) [spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE]

I threw together the quickest simplest Spring Boot restful service I could imagine:

package com.tryme;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TrymeController {
    @RequestMapping(value="/hello")
    public ResponseEntity<String> hello()
    {
        return new ResponseEntity<String>("Hello world.", HttpStatus.OK);
    }
}

The application main is:

package com.tryme;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan
public class TrymeApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {

        SpringApplication.run(TrymeApplication.class, args);

    }

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

I read several blog posts advising how to change your pom to support restful spring services, and this is the POM I created (I tried with both 3.1 servlet and 2.5).

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.tryme</groupId>
    <artifactId>tryme</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>tryme</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.6.RELEASE</version>
    </parent>

    <properties>
        <start-class>com.tryme.TrymeApplication</start-class>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--
        <dependency>  
             <groupId>javax.servlet</groupId>  
             <artifactId>servlet-api</artifactId>  
             <version>2.5</version>  
             <scope>provided</scope>
        </dependency>        
         -->
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-el</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <!--We are building spring boot application with maven-->
    <build>
        <finalName>tryme</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    <!--Repositories for spring libs-->
    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>http://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>http://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <!--Repositories for needed plugins -->
    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>http://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>http://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>    
</project>

I tried forcing headers on my ResponseEntities, and it did't help. Can anyone tell me if there's anything I can change to get this simple app up and running with Spring Rest on 8.x Wildfly? I verified it works fine on 9.x and 10.x

Edit: here is what my latest war looks like. enter image description here

Upvotes: 2

Views: 2442

Answers (1)

andih
andih

Reputation: 5603

There is a bug in Undertow < 1.1.0-Final. Wildfly 8.1 uses Undertow 1.0.15.

In Untertow 1.0.x the getHeaders(String) method does not check the response headers for null.

// from Undertow 1.0.15-final 
// http://grepcode.com/file/repo1.maven.org/maven2/io.undertow/undertow-servlet/1.0.15.Final/io/undertow/servlet/spec/HttpServletResponseImpl.java#HttpServletResponseImpl.getHeaders%28java.lang.String%29
@Override
public Collection<String> getHeaders(final String name) {
    return new ArrayList<String>(exchange.getResponseHeaders().get(name));
}

From Undertow 1.1.0 upwards the getHeaders(String) the methods checks the responseHeaders for null

// from Undertow 1.1.0-final 
// http://grepcode.com/file/repo1.maven.org/maven2/io.undertow/undertow-servlet/1.1.0.Final/io/undertow/servlet/spec/HttpServletResponseImpl.java#HttpServletResponseImpl.getHeader%28java.lang.String%29
@Override
public Collection<String> getHeaders(final String name) {
   HeaderValues headers = exchange.getResponseHeaders().get(name);
    if(headers == null) {
        return Collections.emptySet();
    }
    return new ArrayList<>(headers);
}

Unfortunately I have not found the corresponding bug report in the JBoss / Undertow issue tool.

So the only way to overcome this is to patch / overwrite the io.undertow.servlet.spec.HttpServletResponseImpl

Update the old jars in modules/system/layers/base/io/undertow/ with new ones

  • io.undertow.core
  • io.undertow.servlet

You have to modify the module.xmlin the corresponding directory.

Not sure about

  • io.undertow.jsp
  • io.undertow.websocket

For your example it's enough to update core and servlet. Better upgrade to the latest wildly 8.2 release if possible.

See also discussion on the JBoss forum

Upvotes: 6

Related Questions