Enterprise Java

GWT 2 Spring 3 JPA 2 Hibernate 3.5 Tutorial

This step by step guide will present how to develop a simple web application using Google’s Web Toolkit (GWT) for the rich client and Spring as the back – end, server side framework. The sample web application will provide functionality to make CRUD (Create Retrieve Update Delete) operations to a database. For the data access layer we will use JPA over Hibernate and for a database we will use Hypersonic. Of course you can change the configuration and use whatever you like. We will deploy the web application to an Apache – Tomcat instance.

Our preferred development environment is Eclipse, so as a prerequisite you must have Eclipse with GWT support installed. The installation of the GWT plugin for Eclipse is out of the scope of this tutorial and will not be discussed. Nevertheless you will need the following components :

  1. Eclipse from here
  2. GWT Plugin for Eclipse from here
  3. Spring framework distribution from here
  4. Hibernate persistence framework distribution from here
  5. Hypersonic database from here
  6. Apache commons-logging library from here
  7. AOP Alliance (Java/J2EE AOP Standard) library from here
  8. SLF4J library from here
  9. Apache log4j library from here
  10. Last but not least, download the GWT – Spring “glue” library spring4gwt from here

We will be using Eclipse Galileo, GWT version 2.0.3, Spring version 3.0.1, Hibernate version 3.5.2, Hypersonic version 1.8.1.2, Apache commons-logging version 1.1.1, AOP Alliance (Java/J2EE AOP Standard) version 1.0, SLF4J version 1.5.8, Apache log4j version 1.2.16 and spring4gwt version 0.0.1 for this tutorial.

Enough talk, lets get our hands dirty!

  1. Create a new GWT project, go to File ? New Web Application Project
  2. We will name our project GWTSpring. The base package will be com.javacodegeeks.gwtspring also use only Google Web Toolkit thus uncheck “Use Google App Engine” at the wizard window.

Let’s recap a few things about the GWT project structure

  1. /src folder contains all source files of the application
    • {package_name}.client subpackage contains all source files only available to the client side of the application
    • {package_name}.server subpackage contains all source files only available to the server side part of the application
    • {package_name}.shared subpackage contains all source files available to both the client and server side of the application
  2. /test folder contains all source files for unit tests
  3. /war folder contains essential files for creating a valid web application

In order to properly integrate Spring with GWT at runtime, we must provide all necessary libraries to the web application. So copy the files listed below under /war/WEB-INF/lib (copy the relevant files if you are using different versions)

From Spring distribution

  • /dist/org.springframework.expression-3.0.1.RELEASE-A.jar
  • /dist/org.springframework.beans-3.0.1.RELEASE-A.jar
  • /dist/org.springframework.oxm-3.0.1.RELEASE-A.jar
  • /dist/org.springframework.jms-3.0.1.RELEASE-A.jar
  • /dist/org.springframework.jdbc-3.0.1.RELEASE-A.jar
  • /dist/org.springframework.core-3.0.1.RELEASE-A.jar
  • /dist/org.springframework.context-3.0.1.RELEASE-A.jar
  • /dist/org.springframework.asm-3.0.1.RELEASE-A.jar
  • /dist/org.springframework.aspects-3.0.1.RELEASE-A.jar
  • /dist/org.springframework.transaction-3.0.1.RELEASE-A.jar
  • /dist/org.springframework.context.support-3.0.1.RELEASE-A.jar
  • /dist/org.springframework.aop-3.0.1.RELEASE-A.jar
  • /dist/org.springframework.orm-3.0.1.RELEASE-A.jar
  • /dist/org.springframework.instrument-3.0.1.RELEASE-A.jar
  • /dist/org.springframework.instrument.tomcat-3.0.1.RELEASE-A.jar
  • /dist/org.springframework.test-3.0.1.RELEASE-A.jar
  • /dist/org.springframework.web-3.0.1.RELEASE-A.jar
  • /dist/org.springframework.web.portlet-3.0.1.RELEASE-A.jar
  • /dist/org.springframework.web.servlet-3.0.1.RELEASE-A.jar
  • /dist/org.springframework.web.struts-3.0.1.RELEASE-A.jar

From the Hibernate distribution

  • hibernate3.jar
  • /lib/required/antlr-2.7.6.jar
  • /lib/required/commons-collections-3.1.jar
  • /lib/required/dom4j-1.6.1.jar
  • /lib/required/javassist-3.9.0.GA.jar
  • /lib/required/jta-1.1.jar
  • /lib/required/slf4j-api-1.5.8.jar
  • /lib/jpa/hibernate-jpa-2.0-api-1.0.0.Final.jar
  • /lib/optional/c3p0/c3p0-0.9.1.jar

From the Hypersonic distribution

  • /lib/hsqldb.jar

From the Apache Commons Logging distribution

  • commons-logging-1.1.1.jar

From the AOP Alliance (Java/J2EE AOP Standard) distribution

  • aopalliance.jar

From the SLF4J distribution

  • slf4j-log4j12-1.5.8.jar

From the Apache log4j distribution

  • log4j-1.2.16.jar

The sping4gwt library

  • spring4gwt-0.0.1.jar

We now have to take care of dependences for our Eclipse project. The following jars should be included in the Java build path of the project :

  • hibernate-jpa-2.0-api-1.0.0.Final.jar
  • org.springframework.beans-3.0.1.RELEASE-A.jar
  • org.springframework.context-3.0.1.RELEASE-A.jar
  • org.springframework.core-3.0.1.RELEASE-A.jar
  • org.springframework.orm-3.0.1.RELEASE-A.jar
  • org.springframework.transaction-3.0.1.RELEASE-A.jar

The next step is to provide hooks for the web application so as to load the Spring context upon startup and to allow for spring4gwt to intercept RPC calls between the client and the server and transform them to Spring service invocations.

Locate the web.xml file under /war/WEB-INF and add the following :

For loading the Spring context upon startup,

<listener>
 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

At the servlets section include

<servlet>
 <servlet-name>springGwtRemoteServiceServlet</servlet-name>
 <servlet-class>org.spring4gwt.server.SpringGwtRemoteServiceServlet</servlet-class>
</servlet>

At the servlet-mapping section include, for spring4gwt to intercept RPC calls.

<servlet-mapping>
 <servlet-name>springGwtRemoteServiceServlet</servlet-name>
 <url-pattern>/gwtspring/springGwtServices/*</url-pattern>
</servlet-mapping>

Things to notice here :

  1. The url-pattern child element of the servlet-mapping element for the springGwtRemoteServiceServlet servlet, should be changed to whatever your GWT module name is e.g. {module_name}/springGwtServices/*, the module name is defined in {project_name}.gwt.xml file (here GWTSpring.gwt.xml) located at the root of the base package of the project under /src folder
  2. You can change the name of spring4gwt servlet (springGwtRemoteServiceServlet here) to whatever you like

To continue we have to create the persistence.xml file so as to describe the connection with the database using JPA. The pesistence.xml file must be located inside a META-INF directory which in turn has to be accessible by the web application at runtime (on the classpath). To fulfill the aforementioned requirements we have to create the META-INF folder under the project’s /war/WEB-INF/classes folder. To do so we create a new source folder under the name e.g. “resources” and create the META-INF folder inside it. Finally create the persistence.xml file inside the /resources/META-INF folder. An example persistence.xml is presented below

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
    version="2.0">

    <persistence-unit name="MyPersistenceUnit" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>

        <properties>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
            <property name="hibernate.show_sql" value="false"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
            <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/>
            <property name="hibernate.connection.url" value="jdbc:hsqldb:mem:javacodegeeks"/>
            <property name="hibernate.connection.username" value="sa"/>
            <property name="hibernate.connection.password" value=""/>

            <property name="hibernate.c3p0.min_size" value="5"/>
            <property name="hibernate.c3p0.max_size" value="20"/>
            <property name="hibernate.c3p0.timeout" value="300"/>
            <property name="hibernate.c3p0.max_statements" value="50"/>
            <property name="hibernate.c3p0.idle_test_period" value="3000"/>
        </properties>

    </persistence-unit>

</persistence>

Things to notice here :

  1. If you intent to deploy the web application to a J2EE application server that supports JTA transactions e.g. JBoss or use other databases e.g. Oracle, MySQL etc, please see our “JBoss Spring JPA Hibernate tutorial” here for alternative configurations

Now lets create the applicationContext.xml file that will drive Spring container. Create the file under /war/WEB-INF directory. An example applicationContext.xml is presented below

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:task="http://www.springframework.org/schema/task"
    xsi:schemaLocation="
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
            http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
            http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">

    <context:component-scan base-package="com.javacodegeeks.gwtspring"/>

    <task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>

    <task:executor id="myExecutor" pool-size="5"/>

    <task:scheduler id="myScheduler" pool-size="10"/>

    <tx:annotation-driven/>

    <bean class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean" id="entityManagerFactory">
        <property name="persistenceUnitName" value="MyPersistenceUnit"/>
    </bean>

    <bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

</beans>

Things to notice here :

  1. Change the base-package attribute of the context:component-scan element to whatever is the base package of your project so as to be scanned for Spring components (services, DAOs etc).
  2. Change the value attribute of entityManagerFactory bean persistentUnitName property to the name of your persistent unit as dictated in the persistence.xml file
  3. If you intent to deploy the web application to a J2EE application server that supports JTA transactions e.g. JBoss please see our “JBoss Sping JPA Hibernate tutorial” here for alternative configurations

In the last part of this tutorial we are going to present the Data Transfer Object (DTO) for transferring data between the client and the server, the Data Access Object (DAO) that is used to access the database and the Spring service to expose functionality to the GWT Web client.

The DTO is an object that can be used by both the client and the server, thus you should create a “dto” subpackage under the “shared” package and place the DTO there. We are going to create an EmployeeDTO containing information for an employee like below

package com.javacodegeeks.gwtspring.shared.dto;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "EMPLOYEE")
public class EmployeeDTO implements java.io.Serializable {
    
    private static final long serialVersionUID = 7440297955003302414L;

    @Id
    @Column(name="employee_id")
    private long employeeId;
    
    @Column(name="employee_name", nullable = false, length=30)
    private String employeeName;
    
    @Column(name="employee_surname", nullable = false, length=30)
    private String employeeSurname;
    
    @Column(name="job", length=50)
    private String job;
        
    public EmployeeDTO() {
    }

    public EmployeeDTO(int employeeId) {
        this.employeeId = employeeId;        
    }

    public EmployeeDTO(long employeeId, String employeeName, String employeeSurname,
            String job) {
        this.employeeId = employeeId;
        this.employeeName = employeeName;
        this.employeeSurname = employeeSurname;
        this.job = job;
    }

    public long getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(long employeeId) {
        this.employeeId = employeeId;
    }

    public String getEmployeeName() {
        return employeeName;
    }

    public void setEmployeeName(String employeeName) {
        this.employeeName = employeeName;
    }

    public String getEmployeeSurname() {
        return employeeSurname;
    }

    public void setEmployeeSurname(String employeeSurname) {
        this.employeeSurname = employeeSurname;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

}

The DAO object will be used to access the database and perform CRUD (Create Retrieve Update Delete) operations. It is a server side component so it should be placed under the “server” subpackage of our project. Create a “dao” subpackage and place the DAO there. An example DAO is presented below

package com.javacodegeeks.gwtspring.server.dao;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.javacodegeeks.gwtspring.shared.dto.EmployeeDTO;

@Repository("employeeDAO")
public class EmployeeDAO extends JpaDAO<Long, EmployeeDTO> {
    
    @Autowired
    EntityManagerFactory entityManagerFactory;
    
    @PostConstruct
    public void init() {
        super.setEntityManagerFactory(entityManagerFactory);
    }
    
}

As you can see the EmployeeDAO class extends a basic DAO class (JpaDAO). The EmployeeDAO class can contain specific queries concerning the EmployeeDTO object, but all CRUD operations can be handled from the basic DAO class (JpaDAO). Place the JpaDAO class at the same level as the EmployeeDAO class, under the “dao” subpackage. Below we present the JpaDAO class

package com.javacodegeeks.gwtspring.server.dao; 

import java.lang.reflect.ParameterizedType; 
import java.util.List; 

import javax.persistence.EntityManager; 
import javax.persistence.PersistenceException; 
import javax.persistence.Query; 

import org.springframework.orm.jpa.JpaCallback; 
import org.springframework.orm.jpa.support.JpaDaoSupport; 

public abstract class JpaDAO<K, E> extends JpaDaoSupport { 
    protected Class<E> entityClass; 

    @SuppressWarnings("unchecked") 
    public JpaDAO() { 
        ParameterizedType genericSuperclass = (ParameterizedType) getClass() 
                .getGenericSuperclass(); 
        this.entityClass = (Class<E>) genericSuperclass 
                .getActualTypeArguments()[1]; 
    } 

    public void persist(E entity) { 
        getJpaTemplate().persist(entity); 
    } 

    public void remove(E entity) { 
        getJpaTemplate().remove(entity); 
    } 
    
    public E merge(E entity) { 
        return getJpaTemplate().merge(entity); 
    } 
    
    public void refresh(E entity) { 
        getJpaTemplate().refresh(entity); 
    } 

    public E findById(K id) { 
        return getJpaTemplate().find(entityClass, id); 
    } 
    
    public E flush(E entity) { 
        getJpaTemplate().flush(); 
        return entity; 
    } 
    
    @SuppressWarnings("unchecked") 
    public List<E> findAll() { 
        Object res = getJpaTemplate().execute(new JpaCallback() { 

            public Object doInJpa(EntityManager em) throws PersistenceException { 
                Query q = em.createQuery("SELECT h FROM " + 
                        entityClass.getName() + " h"); 
                return q.getResultList(); 
            } 
            
        }); 
        
        return (List<E>) res; 
    } 

    @SuppressWarnings("unchecked") 
    public Integer removeAll() { 
        return (Integer) getJpaTemplate().execute(new JpaCallback() { 

            public Object doInJpa(EntityManager em) throws PersistenceException { 
                Query q = em.createQuery("DELETE FROM " + 
                        entityClass.getName() + " h"); 
                return q.executeUpdate(); 
            } 
            
        }); 
    }
    
}

Finally we are going to create the service interface and implementation classes for the GWT client to access. The service interface should be accessible by both the client and the server, so it should be placed under the “shared” subpackage of our project. Create a “services” subpackage and place the service interface there. An example interface class follows

package com.javacodegeeks.gwtspring.shared.services;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

import com.javacodegeeks.gwtspring.shared.dto.EmployeeDTO;

@RemoteServiceRelativePath("springGwtServices/employeeService")
public interface EmployeeService extends RemoteService {
    
    public EmployeeDTO findEmployee(long employeeId);
    public void saveEmployee(long employeeId, String name, String surname, String jobDescription) throws Exception;
    public void updateEmployee(long employeeId, String name, String surname, String jobDescription) throws Exception;
    public void saveOrUpdateEmployee(long employeeId, String name, String surname, String jobDescription) throws Exception;
    public void deleteEmployee(long employeeId) throws Exception;
    
}

Things to notice here :

  1. GWT client has to be able to make asynchronous Remote Procedure Calls (RPCs) to the server side service. Thus the service interface must extend the RemoteService interface. An asynchronous counterpart of the specified interface must also be provided to enable asynchronous communication (see below)
  2. We annotate the interface so as to define the URL under which the service will be accessible. As the service is a Spring service we want spring4gwt to intercept the RPC calls and perform a Spring service invocation. To do that we define a relative path that will be handled by “springGwtRemoteServiceServlet” declared in our web.xml as shown above.
  3. The service name declared at “RemoteServiceRelativePath” annotation, here “employeeService”, must match the Spring service bean name. We will define the Spring service bean name in the service implementation class (see below)

The asynchronous counter part of the service interface follows

package com.javacodegeeks.gwtspring.shared.services;

import com.google.gwt.user.client.rpc.AsyncCallback;
import com.javacodegeeks.gwtspring.shared.dto.EmployeeDTO;

public interface EmployeeServiceAsync {

    void deleteEmployee(long employeeId, AsyncCallback<Void> callback);

    void findEmployee(long employeeId, AsyncCallback<EmployeeDTO> callback);

    void saveEmployee(long employeeId, String name, String surname,
            String jobDescription, AsyncCallback<Void> callback);

    void saveOrUpdateEmployee(long employeeId, String name, String surname,
            String jobDescription, AsyncCallback<Void> callback);

    void updateEmployee(long employeeId, String name, String surname,
            String jobDescription, AsyncCallback<Void> callback);

}

The service implementation class is a server side component, so we must place it under “server” subpackage of our project. Create the “services” subpackage and place it there. An example service implementation class is presented below

package com.javacodegeeks.gwtspring.server.services; 

import javax.annotation.PostConstruct; 
import javax.annotation.PreDestroy; 

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Service; 
import org.springframework.transaction.annotation.Propagation; 
import org.springframework.transaction.annotation.Transactional; 

import com.javacodegeeks.gwtspring.server.dao.EmployeeDAO; 
import com.javacodegeeks.gwtspring.shared.dto.EmployeeDTO; 
import com.javacodegeeks.gwtspring.shared.services.EmployeeService; 

@Service("employeeService") 
public class EmployeeServiceImpl implements EmployeeService { 
    
    @Autowired 
    private EmployeeDAO employeeDAO; 

    @PostConstruct 
    public void init() throws Exception { 
    } 
    
    @PreDestroy 
    public void destroy() { 
    } 

    public EmployeeDTO findEmployee(long employeeId) { 
        
        return employeeDAO.findById(employeeId); 
        
    } 
    
    @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class) 
    public void saveEmployee(long employeeId, String name, String surname, String jobDescription) throws Exception { 
            
        EmployeeDTO employeeDTO = employeeDAO.findById(employeeId); 
        
        if(employeeDTO == null) { 
            employeeDTO = new EmployeeDTO(employeeId, name,surname, jobDescription); 
            employeeDAO.persist(employeeDTO); 
        } 
        
    } 
    
    @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class) 
    public void updateEmployee(long employeeId, String name, String surname, String jobDescription) throws Exception { 
        
        EmployeeDTO employeeDTO = employeeDAO.findById(employeeId); 
        
        if(employeeDTO != null) { 
            employeeDTO.setEmployeeName(name); 
            employeeDTO.setEmployeeSurname(surname); 
            employeeDTO.setJob(jobDescription); 
        } 

    } 
    
    @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class) 
    public void deleteEmployee(long employeeId) throws Exception { 
        
        EmployeeDTO employeeDTO = employeeDAO.findById(employeeId); 
        
        if(employeeDTO != null) 
            employeeDAO.remove(employeeDTO); 

    } 
    
    @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class) 
    public void saveOrUpdateEmployee(long employeeId, String name, String surname, String jobDescription) throws Exception { 
        
        EmployeeDTO employeeDTO = new EmployeeDTO(employeeId, name,surname, jobDescription); 
        
        employeeDAO.merge(employeeDTO); 
        
    } 

} 

Things to notice here :

  1. We use the @Service(“employeeService”) stereotype annotation so as to declare that this class represents a Spring service by the name “exampleService”. The Spring container will instantiate all services at start up.
  2. We use the @Autowire annotation to inject the instance of the DAO class to the “employeeService”. For proper instantiation of the service Spring container has to resolve first all potential references among services, so it instantiates the DAO class and injects the instance to the appropriate field of “employeeService” – the “employeeDAO” field. In case you wonder, the dependency injection is done according to type (Class) and if not satisfied according to name, meaning that if we have defined multiple services of the same type (Class) the one injected would be the one with the same name as the designated field.
  3. We use the Java annotations @PostConstruct and @PreDestroy to declare the methods that will be invoked by Spring container after initialization (all dependency injection is done) and prior destruction of the service.
  4. We use the @Transactional annotation for all methods that need to perform update operation on the database (INSERT, UPDATE, DELETE)
  5. We DO NOT use the @Transactional annotation on methods that perform retrieve (FIND) operations on the database (except for objects that contain lazily initialized references – see below), and/or perform no database operations. That is because every time you invoke a method annotated as transactional, Spring container involves in the invocation JPA‘s entity manager and as a consequence platform’s transaction manager, so as to define the transactional behavior that will be applied, introducing a noticeable performance penalty especially for low latency / high throughput applications
  6. For methods that perform retrieve (FIND) operations for objects that contain lazily initialized references you should use the @Transactional annotation, designating “NESTED” propagation type in order for Spring to maintain Hibernate session open for the entire method call
  7. Transactional behavior is applied only on client calls to the service. Transactional behavior is not applied to intra operation calls. For example if a client invokes an operation that is not annotated as transactional and the implementation of the latter introduces a call to another operation of the same service that is annotated transactional then for the combined operations no transactional behavior will be applied

We are almost done!, we have to develop the GWT user interface to access our Spring service. Despite the fact that GWT user interface development is out of the scope of this tutorial we are going to provide a basic user interface just to show a couple of Spring service invocations.

Locate the entry point of your GWT application. The file should be named like {project_name}.java, in our case GWTSpring.java, and located under “client” subpackage or our main package. Alter the entry point class as shown below

package com.javacodegeeks.gwtspring.client;

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.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.javacodegeeks.gwtspring.shared.dto.EmployeeDTO;
import com.javacodegeeks.gwtspring.shared.services.EmployeeService;
import com.javacodegeeks.gwtspring.shared.services.EmployeeServiceAsync;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class GWTSpring implements EntryPoint {
    /**
     * The message displayed to the user when the server cannot be reached or
     * returns an error.
     */
    private static final String SERVER_ERROR = "An error occurred while "
            + "attempting to contact the server. Please check your network "
            + "connection and try again. The error is : ";
    
    /**
     * Create a remote service proxy to talk to the server-side Employee service.
     */
    private final EmployeeServiceAsync employeeService = GWT
            .create(EmployeeService.class);

    /**
     * This is the entry point method.
     */
    public void onModuleLoad() {
        final Button saveOrUpdateButton = new Button("SaveOrUpdate");
        final Button retrieveButton = new Button("Retrieve");
        final TextBox employeeInfoField = new TextBox();
        employeeInfoField.setText("Employee Info");
        final TextBox employeeIdField = new TextBox();
        final Label errorLabel = new Label();

        // We can add style names to widgets
        saveOrUpdateButton.addStyleName("sendButton");
        retrieveButton.addStyleName("sendButton");

        // Add the nameField and sendButton to the RootPanel
        // Use RootPanel.get() to get the entire body element
        RootPanel.get("employeeInfoFieldContainer").add(employeeInfoField);
        RootPanel.get("updateEmployeeButtonContainer").add(saveOrUpdateButton);
        RootPanel.get("employeeIdFieldContainer").add(employeeIdField);
        RootPanel.get("retrieveEmployeeButtonContainer").add(retrieveButton);
        RootPanel.get("errorLabelContainer").add(errorLabel);

        // Focus the cursor on the name field when the app loads
        employeeInfoField.setFocus(true);
        employeeInfoField.selectAll();

        // Create the popup dialog box
        final DialogBox dialogBox = new DialogBox();
        dialogBox.setText("Remote Procedure Call");
        dialogBox.setAnimationEnabled(true);
        final Button closeButton = new Button("Close");
        // We can set the id of a widget by accessing its Element
        closeButton.getElement().setId("closeButton");
        final Label textToServerLabel = new Label();
        final HTML serverResponseLabel = new HTML();
        VerticalPanel dialogVPanel = new VerticalPanel();
        dialogVPanel.addStyleName("dialogVPanel");
        dialogVPanel.add(new HTML("<b>Sending request to the server:</b>"));
        dialogVPanel.add(textToServerLabel);
        dialogVPanel.add(new HTML("
<b>Server replies:</b>"));
        dialogVPanel.add(serverResponseLabel);
        dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
        dialogVPanel.add(closeButton);
        dialogBox.setWidget(dialogVPanel);

        // Add a handler to close the DialogBox
        closeButton.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                dialogBox.hide();
                saveOrUpdateButton.setEnabled(true);
                saveOrUpdateButton.setFocus(true);
                retrieveButton.setEnabled(true);
            }
        });

        // Create a handler for the saveOrUpdateButton and employeeInfoField
        class SaveOrUpdateEmployeeHandler implements ClickHandler, KeyUpHandler {
            /**
             * Fired when the user clicks on the saveOrUpdateButton.
             */
            public void onClick(ClickEvent event) {
                sendEmployeeInfoToServer();
            }

            /**
             * Fired when the user types in the employeeInfoField.
             */
            public void onKeyUp(KeyUpEvent event) {
                if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
                    sendEmployeeInfoToServer();
                }
            }

            /**
             * Send the employee info from the employeeInfoField to the server and wait for a response.
             */
            private void sendEmployeeInfoToServer() {
                // First, we validate the input.
                errorLabel.setText("");
                String textToServer = employeeInfoField.getText();

                // Then, we send the input to the server.
                saveOrUpdateButton.setEnabled(false);
                textToServerLabel.setText(textToServer);
                serverResponseLabel.setText("");

                String[] employeeInfo = textToServer.split(" ");
                
                long employeeId = Long.parseLong(employeeInfo[0]);
                String employeeName = employeeInfo[1];
                String employeeSurname = employeeInfo[2];
                String employeeJobTitle = employeeInfo[3];
                
                employeeService.saveOrUpdateEmployee(employeeId, employeeName, employeeSurname, employeeJobTitle, 
                        new AsyncCallback<Void>() {
                            public void onFailure(Throwable caught) {
                                // Show the RPC error message to the user
                                dialogBox
                                        .setText("Remote Procedure Call - Failure");
                                serverResponseLabel
                                        .addStyleName("serverResponseLabelError");
                                serverResponseLabel.setHTML(SERVER_ERROR + caught.toString());
                                dialogBox.center();
                                closeButton.setFocus(true);
                            }

                            public void onSuccess(Void noAnswer) {
                                dialogBox.setText("Remote Procedure Call");
                                serverResponseLabel
                                        .removeStyleName("serverResponseLabelError");
                                serverResponseLabel.setHTML("OK");
                                dialogBox.center();
                                closeButton.setFocus(true);
                            }
                        });
            }
        }
        
        // Create a handler for the retrieveButton and employeeIdField
        class RetrieveEmployeeHandler implements ClickHandler, KeyUpHandler {
            /**
             * Fired when the user clicks on the retrieveButton.
             */
            public void onClick(ClickEvent event) {
                sendEmployeeIdToServer();
            }

            /**
             * Fired when the user types in the employeeIdField.
             */
            public void onKeyUp(KeyUpEvent event) {
                if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
                    sendEmployeeIdToServer();
                }
            }

            /**
             * Send the id from the employeeIdField to the server and wait for a response.
             */
            private void sendEmployeeIdToServer() {
                // First, we validate the input.
                errorLabel.setText("");
                String textToServer = employeeIdField.getText();

                // Then, we send the input to the server.
                retrieveButton.setEnabled(false);
                textToServerLabel.setText(textToServer);
                serverResponseLabel.setText("");

                employeeService.findEmployee(Long.parseLong(textToServer),  
                        new AsyncCallback<EmployeeDTO>() {
                            public void onFailure(Throwable caught) {
                                // Show the RPC error message to the user
                                dialogBox
                                        .setText("Remote Procedure Call - Failure");
                                serverResponseLabel
                                        .addStyleName("serverResponseLabelError");
                                serverResponseLabel.setHTML(SERVER_ERROR + caught.toString());
                                dialogBox.center();
                                closeButton.setFocus(true);
                            }

                            public void onSuccess(EmployeeDTO employeeDTO) {
                                dialogBox.setText("Remote Procedure Call");
                                serverResponseLabel
                                        .removeStyleName("serverResponseLabelError");
                                if(employeeDTO != null)
                                    serverResponseLabel.setHTML("Employee Information Id : " + employeeDTO.getEmployeeId() + " Name : " + employeeDTO.getEmployeeName() + " Surname : " + employeeDTO.getEmployeeSurname() + " Job Title : " + employeeDTO.getJob());
                                else
                                    serverResponseLabel.setHTML("No employee with the specified id found");
                                dialogBox.center();
                                closeButton.setFocus(true);
                            }
                        });
            }
        }

        // Add a handler to send the employee info to the server
        SaveOrUpdateEmployeeHandler saveOrUpdateEmployeehandler = new SaveOrUpdateEmployeeHandler();
        saveOrUpdateButton.addClickHandler(saveOrUpdateEmployeehandler);
        employeeInfoField.addKeyUpHandler(saveOrUpdateEmployeehandler);
        
        // Add a handler to send the employee id to the server
        RetrieveEmployeeHandler retrieveEmployeehandler = new RetrieveEmployeeHandler();
        retrieveButton.addClickHandler(retrieveEmployeehandler);
        employeeIdField.addKeyUpHandler(retrieveEmployeehandler);
    }
}

As you can see, Spring service invocations are performed just like classic GWT service invocations, transparently to the client.

Finally locate the main web page for your project. The file should be named like {project_name}.html, in our case GWTSpring.html, and located under /war folder of our project. Alter the main web page as shown below

<!doctype html>
<!-- The DOCTYPE declaration above will set the    -->
<!-- browser's rendering engine into               -->
<!-- "Standards Mode". Replacing this declaration  -->
<!-- with a "Quirks Mode" doctype may lead to some -->
<!-- differences in layout.                        -->

<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">

    <!--                                                               -->
    <!-- Consider inlining CSS to reduce the number of requested files -->
    <!--                                                               -->
    <link type="text/css" rel="stylesheet" href="GWTSpring.css">

    <!--                                           -->
    <!-- Any title is fine                         -->
    <!--                                           -->
    <title>Spring GWT Web Application Starter Project</title>
    
    <!--                                           -->
    <!-- This script loads your compiled module.   -->
    <!-- If you add any GWT meta tags, they must   -->
    <!-- be added before this line.                -->
    <!--                                           -->
    <script type="text/javascript" language="javascript" src="gwtspring/gwtspring.nocache.js"></script>
  </head>

  <!--                                           -->
  <!-- The body can have arbitrary html, or      -->
  <!-- you can leave the body empty if you want  -->
  <!-- to create a completely dynamic UI.        -->
  <!--                                           -->
  <body>

    <!-- OPTIONAL: include this if you want history support -->
    <iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>
    
    <!-- RECOMMENDED if your web app will not function without JavaScript enabled -->
    <noscript>
      <div style="width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif">
        Your web browser must have JavaScript enabled
        in order for this application to display correctly.
      </div>
    </noscript>

    <h1>Spring GWT Web Application Starter Project</h1>

    <table align="center">
      <tr>
        <td colspan="2" style="font-weight:bold;">Please enter employee info (id name surname job):</td>        
      </tr>
      <tr>
        <td id="employeeInfoFieldContainer"></td>
        <td id="updateEmployeeButtonContainer"></td>
      </tr>
      <tr>
      <tr>
        <td colspan="2" style="font-weight:bold;">Please enter employee id:</td>        
      </tr>
      <tr>
        <td id="employeeIdFieldContainer"></td>
        <td id="retrieveEmployeeButtonContainer"></td>
      </tr>
      <tr>
        <td colspan="2" style="color:red;" id="errorLabelContainer"></td>
      </tr>
    </table>
  </body>
</html>

To compile the application, right click on the project name and select Run As ? Compile GWT Application

To deploy the web application just copy the /war folder in Apache – Tomcat “webapps” folder. You can change the name of the war folder to whatever you like, preferably rename it after the project name e.g. GWTSpring

To launch the application point your browser to the following address

http://localhost:8080/GWTSpring/

If all went well you should see your main web page. Two text boxes should be displayed each followed by a button. In the first text box you can save or update an employee to the database. Provide as input the id, the name, the surname, and a job description separated by a space character. Clicking on the “SaveOrUpdate” button the provided information will be stored to the database. For existing employee entries (same id) an update will be performed. The second text box is used to retrieve existing employee entries. Provide an employee id and click on the “Retrieve” button. If the employee exists you should see the employee id, name, surname and job description.

Few, that was a big tutorial!

You can download the project from here (required 3rd party libraries as described at the beginning are not included)

Hope you liked it

Justin

Related Articles :
Related Snippets :

Byron Kiourtzoglou

Byron is a master software engineer working in the IT and Telecom domains. He is an applications developer in a wide variety of applications/services. He is currently acting as the team leader and technical architect for a proprietary service creation and integration platform for both the IT and Telecom industries in addition to a in-house big data real-time analytics solution. He is always fascinated by SOA, middleware services and mobile development. Byron is co-founder and Executive Editor at Java Code Geeks.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

13 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Monoranjan
Monoranjan
11 years ago

I am getting the following exception while running the test case for Employee Service. Any help on this is appreciated.

[WARN] com.google.gwt.junit.server.JUnitHostImpl-10213237: An IncompatibleRemoteServiceException was thrown while processing this call.

com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException: This application is out of date, please click the refresh button on your browser. ( Blocked attempt to access interface ‘com.javacodegeeks.SpringGWT.shared.services.EmployeeService’, which is not implemented by ‘com.google.gwt.junit.server.JUnitHostImpl’; this is either misconfiguration or a hack attempt )

at com.google.gwt.user.server.rpc.RPC.decodeRequest(RPC.java:254)

at com.google.gwt.user.server.rpc.HybridServiceServlet.processCall(HybridServiceServlet.java:126)

at com.google.gwt.user.server.rpc.HybridServiceServlet.processCall(HybridServiceServlet.java:113)

at com.google.gwt.rpc.server.RpcServlet.processPost(RpcServlet.java:233)

at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:62)

Mohammad
Mohammad
9 years ago
Reply to  Monoranjan

Did u fix this error, as i’m getting the same now for another project

kool
kool
10 years ago

I have this exception when i try to run the application :

java.lang.NoClassDefFoundError: javax/validation/ParameterNameProvider

Can you help me?

Nick
Nick
10 years ago

Nothing displayed except the two static text boxes :/ Looks like GWT doesn’t load the application.

Gianluca Pasqualicchio
Gianluca Pasqualicchio
10 years ago

Don’t work.

Carlos
10 years ago

Please provide a screenshot of what the code is doing here… thnaks!

Karen
Karen
10 years ago

Thanks for this good tutorial and full description. It takes two days to get it work for my Configurations. I use a MySQL database and hibernate.
My persistence.xml has to be adapted in this way:

And I must add hibernate-validator to /war/lib (classmate-0.8.0.jar,hibernate-validator-5.0.1.Final.jar,javax.el-2.2.4.jar,javax.el-api-2.2.4.jar,jboss-logging-3.1.1.GA.jar,validation-api-1.1.0.Final.jar)!

Good Tutorial to get started!

Tharaka
Tharaka
10 years ago
Reply to  Karen

Your project is working?

Tharaka
Tharaka
10 years ago

Error here,
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/tx]
Offending resource: ServletContext resource [/WEB-INF/applicationContext.xml]

any one can help me..

SpringNewPro
SpringNewPro
9 years ago

Hi there,

I downloaded the whole project and inserted the needed libs.
Now I keep getting this error:
>>> Bean property ‘persistenceUnitName’ is not writable or has an invalid setter method.
Does the parameter type of the setter match the return type of the getter?

Has anyone an idea why is this happening?
Thanks in advanced. :-)

Diby
Diby
8 years ago

this class:
public class EmployeeServiceImpl implements EmployeeService,

must extends RemoteServiceServlet so it should be

public class EmployeeServiceImpl extends RemoteServiceServlet implements EmployeeService{
}

Savani
Savani
7 years ago

Hello,

Please create this project as a Mavenized project.

Marwen_Bta
Marwen_Bta
6 years ago

Hello,

I’m doing this tutorial but I’ve had some mistakes in
the following classes:
EmployeeDAO et JpaDAO.
I can’t download the project, the link is dead.
Could anyone send me the project?

Back to top button