Search code examples
spring-bootjerseyjax-rsspring-data-jpawildfly

NPE in jax-rs service for jpa query in Spring Boot + Wildfly 10


I have a simple service on Spring Boot with JAX-RS, JPA. It works fine on Tomcat, but when I deployed in WildFly 10, I get NPE (and Err 500 on web-page) for Services, which used Jpa query for DB. Service that doesn't use jpa-query work fine. Here is my configuration:

pom.xml:

groupId>com.github.naut92.animalswebservice</groupId>
    <artifactId>animalsrestservice</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>AnimalsRestService</name>
    <description>Restful project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <start-class>com.github.naut92.animalswebservice.AnimalsRestServiceApplication</start-class>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jersey</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>

        </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-web-services</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- thymeleaf used case-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!-- tag::security[] -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!-- end::security[] -->
    </dependencies>

    <build>
        <!--name project for deploying in Enterprise Servers-->
        <finalName>animals-rest</finalName>
        <plugins>
            <!--plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin-->


            <plugin>
                <groupId>org.jboss.as.plugins</groupId>
                <artifactId>jboss-as-maven-plugin</artifactId>
                <version>7.9.Final</version>
                <executions>
                    <execution>
                        <id>jboss-deploy</id>
                        <phase>pre-integration-test</phase>
                        <goals>
                            <goal>deploy</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>jboss-undeploy</id>
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>undeploy</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>

    <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>
    <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>

JerseyConfig class:

@ApplicationPath("/index")
@Configuration
    public class JerseyConfig extends ResourceConfig /*Application*/ {
        public JerseyConfig() {
            register(CustomerResource.class);
        }

Service class:

@Component
@Path("/cust")
public class CustomerResource {
   
    @Inject
    private /*final*/ CustomersRepository customersRepository;

    public CustomerResource(CustomersRepository customersRepository){
        this.customersRepository = customersRepository;
    }

    public CustomerResource() {
    }

    //TEST--------------------------------
    @GET
    @Path(value = "/ajaxtest")
    @Produces("application/json")
    public Set<String> ajaxTest() {
        Set<String> records = new HashSet<String>();
        records.add("Record #1");
        records.add("Record #2");

        return records;
    }

    @GET
    @Path(value = "/all")
    @Produces("application/json")
    public Collection<CustomersEntity> listCustomers() {
        return (Collection<CustomersEntity>) customersRepository.findAll();
    }

    @GET
    @Path("/all/{id}")
    @Produces("application/json")
    public CustomersEntity getCustomerById(@PathParam(value = "id") Long id) throws NotFoundException {
        CustomersEntity customer;
        if(customersRepository.exists(id)){
        customer = customersRepository.findOne(id);
        }
        else
            throw new NotFoundException("could not find user '" + id + "'.");
        return customer;
    }
}

and ajaxTest() method works fine, but the other two don't work:

java.lang.NullPointerException
    at com.github.naut92.animalswebservice.resourcesService.CustomerResource.listCustomers(CustomerResource.java:58)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:139)
    at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:295)
    at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:249)
    at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:236)
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:402)

I try to used <exclusions> for spring-boot-starter-tomcat like in this tutorial and spring-boot-starter-websocket, but I aways get the same error.


Solution

  • It's because Jersey isn't being used. You can tell be the exception that RESTEasy (Wildlfy's implementation of JAX-RS) is being used. The reason is because of the @ApplicationPath annotation on the ResourceConfig class. ResourceConfig is an extension of the standard JAX-RS Application class. And JAX-RS is designed to pick up an Application class that is annotated with @ApplicationPath.

    So on startup, RESTEasy is creating the application, not Spring Boot. This is the reason for the NPE. RESTEasy creates the resource class, calling the default constructor, where the service is not initialized. And you are trying to call a non-initialized service during the request.

    Now I don't know if this will work, as I've never tried to deploy to Wildfly, but the main things you need to fix are:

    1. Remove the @ApplicationPath so that RESTEasy doesn't pick it up. You can configure the path in your applicaiton.properties file. Just use the property spring.jersey.application-path

    2. You need to make sure your Spring Boot "application" class extends SpringBootServletInitializer. This allows the application to be picked up by the container on startup. This is how Spring Boot will initialize the application.