nafanya
nafanya

Reputation: 21

GWT + Spring Boot @Autowired is not working

I have created a application in GWT + Spring Boot and when I try to use @Autowired - I get NullPointerException. Apparently @Autowired does not work and does not insert the bean in the right place. How can I fix this situation?

Exception while dispatching incoming RPC call

com.google.gwt.user.server.rpc.UnexpectedException: Service method 'public abstract java.util.List com.myGWT.springbootapp.client.UserService.list()' threw an unexpected exception: java.lang.NullPointerException

File web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <servlet>
        <servlet-name>userService</servlet-name>
        <servlet-class>com.myGWT.springbootapp.server.UserServiceImpl</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>userService</servlet-name>
        <url-pattern>/gwtApp/service</url-pattern>
    </servlet-mapping>


    <welcome-file-list>
        <welcome-file>GWTApp.html</welcome-file>
    </welcome-file-list>
</web-app>

pom.xml

<name>spring-boot-app</name>
    <description>Demo 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>
        <gwt.version>2.7.0</gwt.version>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.8</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.8</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.logging.log4j</groupId>
                    <artifactId>log4j-api</artifactId>
                </exclusion>
                <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</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

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


        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>11.2.0.4</version>
        </dependency>

        <!-- GWT -->
        <dependency>
            <groupId>com.google.gwt</groupId>
            <artifactId>gwt-user</artifactId>
            <version>${gwt.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.google.gwt</groupId>
            <artifactId>gwt-servlet</artifactId>
            <version>${gwt.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.google.gwt</groupId>
            <artifactId>gwt-codeserver</artifactId>
            <version>${gwt.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.google.gwt</groupId>
            <artifactId>gwt-dev</artifactId>
            <version>${gwt.version}</version>
            <scope>provided</scope>
            <exclusions>
            <exclusion>
                <artifactId>apache-jsp</artifactId>
                <groupId>org.eclipse.jetty</groupId>
            </exclusion>
            </exclusions>
        </dependency>
    </dependencies>



    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>gwt-maven-plugin</artifactId>
                    <version>${gwt.version}</version>
                </plugin>
                <plugin>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>2.5</version>
                </plugin>
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>2.4.1</version>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <artifactId>maven-clean-plugin</artifactId>
                <configuration>
                    <filesets>
                        <fileset>
                            <directory>target</directory>
                        </fileset>
                        <fileset>
                            <directory>builds</directory>
                            <includes>
                                <include>**/*.*</include>
                            </includes>
                        </fileset>
                    </filesets>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-source-plugin</artifactId>
                <version>2.4</version>
                <executions>
                    <execution>
                        <id>attach-sources</id>
                        <phase>deploy</phase>
                        <goals>
                            <goal>jar-no-fork</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-deploy-plugin</artifactId>
                <version>2.8.2</version>
                <executions>
                    <execution>
                        <id>deploy</id>
                        <phase>deploy</phase>
                        <goals><goal>deploy</goal></goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

File UserServiceImpl.java

package com.myGWT.springbootapp.server;

import com.myGWT.springbootapp.client.UserService;
import com.myGWT.springbootapp.entities.User;
import com.myGWT.springbootapp.repositories.UserRepository;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl extends RemoteServiceServlet implements UserService {

AnnotationConfigWebApplicationContext();

    @Autowired
    private UserRepository repository ;

    @Override
    public List<User> list() {
        return repository.findAll();
    }
}

File UserRepository.java

package com.myGWT.springbootapp.repositories;


import com.myGWT.springbootapp.entities.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.List;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {

}

File UserService.java

package com.myGWT.springbootapp.client;

import com.myGWT.springbootapp.entities.User;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;


import java.util.List;

@RemoteServiceRelativePath("service")
public interface UserService extends RemoteService {

  List<User> list();
}

File GWTApp.java

package com.myGWT.springbootapp.client;

import com.myGWT.springbootapp.entities.User;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.*;
import com.google.gwt.view.client.ListDataProvider;

import java.util.List;

public class GWTApp implements EntryPoint {

    private  UserServiceAsync userService = GWT.create(UserService.class);

    private ListDataProvider<User> createTable (CellTable<User> table){
        TextColumn<User> idColumn =new TextColumn<User>() {
            @Override
            public String getValue(User object) {
                return object.getId().toString();
            }
        };
        TextColumn<User> loginColumn =new TextColumn<User>() {
            @Override
            public String getValue(User object) {
                return object.getLogin();
            }
        };
        table.addColumn(idColumn, "Id");
        table.addColumn(loginColumn, "Login");
        final ListDataProvider<User> dataProvider = new ListDataProvider<>();
        dataProvider.addDataDisplay(table);
        this.userService.list(new AsyncCallback<List<User>>() {
            @Override
            public void onFailure(Throwable caught) {
                Window.alert("Error: " + caught.getMessage());
            }

            @Override
            public void onSuccess(List<User> result) {
                dataProvider.getList().addAll(result);

            }
        });
        return dataProvider;
    }

    public void onModuleLoad() {
        CellTable<User> table = new CellTable<>();
        ListDataProvider<User> dataProvider = createTable(table);

        RootPanel.get().add(table);
    }
}

Directory structure of the project

enter image description here

Upvotes: 2

Views: 861

Answers (1)

Jon Sampson
Jon Sampson

Reputation: 1551

The short answer is: Spring didn't instantiate your GWT service; therefore it can't autowire anything into it.

The Jetty container is actually responsible for creating and mapping an instance of UserServiceImpl. That's what your web.xml is describing.

Your @Service annotation is indeed creating a Servlet instance within the Spring context as you would expect, but it's not known to Jetty. Historically, I have removed the @Service annotation from my RemoteServiceServlet-based services and added the following method to provide @Autowire support:

public class UserServiceImpl extends RemoteServiceServlet implements UserService {

...

     @Override
     public void init() throws ServletException {
          super.init();
          SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, getServletContext());
     }

...

}

From the SpringBeanAutowiringSupport javadoc:

Process @Autowired injection for the given target object, based on the current root web application context as stored in the ServletContext.

Jetty will call init() during Servlet creation and boom, you're beans come together. This is on the hacky side of things because it bridges two unrelated processes, but it is working for me in production currently.

Using a Servlet 3.0+ compliant container you can probably find a way to use WebApplicationInitializer(javadoc) to fire up your Spring context via AnnotationConfigWebApplicationContext(javadoc) prior to mapping your servlets which would allow you to use the instances from there (this replaces your web.xml).

Edit:

And since you're using Spring Boot you can take a peek at this thread which shows how to map servlets via ServletRegistrationBean: How can I register a secondary servlet with Spring Boot? (see checketts's answer)

Upvotes: 2

Related Questions