Saturday, 3 July 2010

GWT 2 Spring 3 JPA 2 Hibernate 3.5 Tutorial – Eclipse and Maven 2 showcase


A little while ago a friend and colleague of mine winged at me saying „Only half of the world is using Maven“. His statement struck me like a thunderbolt when I realized that our most popular article (up until now) GWT 2 Spring 3 JPA 2 Hibernate 3.5 Tutorial presents a GWTSpring integration approach based on Google's Web Toolkit (GWT) Eclipse plugin, lacking all advantages that Maven provides. My colleague was right, Maven is the „de facto“ standard software project management and comprehension tool. Maven can manage a project's build, reporting and documentation from a central piece of information, the project object model (POM) file.

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.

Maven comes in two flavors, the standalone tool with command line support, and as an IDE (Eclipse and Netbeans) integration plugin. Our preferred development environment is Eclipse, so as a prerequisite you must have Eclipse with Maven support installed. The installation of Maven 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. Maven Plugin for Eclipse from here
  3. GWTSpring “glue” library spring4gwt from here
We will be using Eclipse Galileo and „m2eclipse“ Maven Integration for Eclipse Plugin version 0.10.0, GWT version 2.0.3, Spring version 3.0.1, Hibernate version 3.5.1, Hypersonic version 1.8.0.10, slf4j-log4j12 version 1.5.8, c3p0 connection pool version 0.9.1.2 and spring4gwt version 0.0.1 for this tutorial.

Enough talk, lets get our hands dirty!
  1. Create a new Maven project, go to File → Project → Maven → Maven Project
  2. In the „Select project name and location“ page of the wizard, make sure that „Create a simple project (skip archetype selection)“ option is unchecked, hit „Next“ to continue with default values
  3. In the „Select an Archetype“ page of the wizard, select „Nexus Indexer“ at the „Catalog“ drop down list and after the archetypes selection area is refreshed, select the „gwt-maven-plugin“ archetype from „org.codehaus.mojo“ to use. You can use the „filter“ text box to narrow search results. Hit „Next“ to continue
  4. In the „Enter an artifact id“ page of the wizard, you can define the name and main package of your project. We will set the „Group Id“ variable to „com.javacodegeeks“ and the „Artifact Id“ variable to „gwtspring“. The aforementioned selections compose the main project package as „com.javacodegeeks.gwtspring“ and the project name as „gwtspring“. Hit „Finish“ to exit the wizard and to create your project
Let's recap a few things about the Maven GWT project structure
  1. /src/main/java folder contains source files for the dynamic content of the application
    • {main_package}.client sub-package contains source files only available to the client side of the application
    • {main_package}.server sub-package contains source files only available to the server side part of the application (this sub-package is not automatically created upon project creation)
    • {main_package}.shared sub-package contains source files available to both the client and server side of the application (this sub-package is not automatically created upon project creation)
  2. /src/main/resources contains source files for static content e.g. static html pages and resource files of the application e.g. css files
  3. /src/test/java folder contains all source files for unit tests
  4. /src/main/webapp folder contains essential files for creating a valid web application, e.g. „web.xml“
  5. /target folder contains the compiled and packaged deliverables
  6. /war folder is used in the build and package process
  7. The „pom.xml“ is the project object model (POM) file. The single file that contains all project related configuration
In order to properly integrate Spring with GWT at runtime, we must provide all necessary libraries to the web application. Open the graphical editor of your „pom.xml“ and perform the following changes :
  1. Locate the „Properties“ section at the „Overview“ page of the POM editor and perform the following changes:
    • Create a new property with name org.springframework.version and value 3.0.1.RELEASE
    • Create a new property with name org.hibernate.version and value 3.5.1-Final
    • Alter gwt.version propery's value to 2.0.3
    • Alter maven.compiler.source and maven.compiler.target properties values according to the version of your Java runtime environment, we will use 1.6
  2. Navigate to the „Dependencies“ page of the POM editor and create the following dependencies (you should fill the „GroupId“, „Artifact Id“ and „Version“ fields of the „Dependency Details“ section at that page) :
    • Group Id : org.springframework Artifact Id : spring-orm Version : ${org.springframework.version}
    • Group Id : org.springframework Artifact Id : spring-web Version : ${org.springframework.version}
    • Group Id : org.hibernate Artifact Id : hibernate-core Version : ${org.hibernate.version}
    • Group Id : org.hibernate Artifact Id : hibernate-annotations Version : ${org.hibernate.version}
    • Group Id : org.hibernate Artifact Id : hibernate-entitymanager Version : ${org.hibernate.version}
    • Group Id : org.hibernate.javax.persistence Artifact Id : hibernate-jpa-2.0-api Version : 1.0.0.Final
    • Group Id : org.slf4j Artifact Id : slf4j-log4j12 Version : 1.5.8
    • Group Id : org.hsqldb Artifact Id : hsqldb Version : 1.8.0.10
    • Group Id : c3p0 Artifact Id : c3p0 Version : 0.9.1.2
  3. Locate the "Show Advanced Tabs" button at the top right of your POM editor and click it. Navigate to the "Repositories" page and create the following repository (you should fill the „Id" and „URL“ fields of the „Repository Details“ section at that page) :
    • Id : JBoss URL : http://repository.jboss.org/maven2/
As you can see Maven manages library dependencies declaratively. A local repository is created (by default under {user_home}/.m2 folder) and all required libraries are downloaded and placed there from public repositories. Furthermore intra – library dependencies are automatically resolved and manipulated. Nevertheless not all libraries are available to public repositories. In that case we must download the required library by hand and use it in our project. Such a case is the „spring4gwt“ library. To use it we must create a „lib“ folder under /src/main/webapp/WEB-INF folder and place „spring4gwt-0.0.1.jar“ there. In a future article we will discuss how to create you own Maven shared repository and install libraries that are not publicly available, so stay tuned!

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 /src/main/webapp/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>/com.javacodegeeks.gwtspring.Application/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 by default {main_package}.Application
  2. You can change the name of spring4gwt servlet (springGwtRemoteServiceServlet here) to whatever you like
The next step is 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 /src/main/resources folder. Finally create the „persistence.xml“ file inside the /src/main/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>
Now lets create the applicationContext.xml file that will drive Spring container. Create the file under /src/main/webapp/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" />

 <tx:annotation-driven />

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

 <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
  <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
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 „shared.dto“ sub-package under your main package (in our case com.javacodegeeks.gwtspring) 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;
 }

}
For the DTO object to be available to the GWT client we must instruct the GWT compiler to parse it. To do so, locate the main GWT module file, it should be named „Application.gwt.xml“ and located under your main package (in our case com.javacodegeeks.gwtspring) and append the following directives :
<!-- Specify the paths for translatable code          -->
<source path='client'/>
<source path='shared'/>
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 a “server” sub-package of our project. Create a “server.dao” sub-package under your main project package (in our case com.javacodegeeks.gwtspring) 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” sub-package of our project. Create a “services” sub-package 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 is automatically created by Maven before project compilation and packaging faces
  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 service implementation class is a server side component, so we must place it under “server” sub-package of our project. Create the “services” sub-package 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. That is because Spring uses AOP proxy classes to enforce transactional behavior
We are almost done!, we have to develop the GWT user interface to access our Spring service.

Locate the entry point of your GWT application. The file should be named „Application.java“ and located under “client” sub-package 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.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.EmployeeServiceAsync;


/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class Application
    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 = EmployeeServiceAsync.Util.getInstance();

 /**
  * 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("<br><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 <br>Id : " + employeeDTO.getEmployeeId() + "<br>Name : " + employeeDTO.getEmployeeName() + "<br>Surname : " + employeeDTO.getEmployeeSurname() + "<br>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 „Application.html“ and located under /src/main/resources/{main_package}/public folder of your project (in our case /src/main/resources/com/javacodegeeks/gwtspring/public). Alter the main web page as shown below
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<!-- The HTML 4.01 Transitional DOCTYPE declaration-->
<!-- above set at the top of the file will set     -->
<!-- the browser's rendering engine into           -->
<!-- "Quirks Mode". Replacing this declaration     -->
<!-- with a "Standards Mode" doctype is supported, -->
<!-- but may lead to some differences in layout.   -->

<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <!--                                           -->
    <!-- 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="com.javacodegeeks.gwtspring.Application.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 align="center">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 build the application right click on your project → Run As → Maven package

To deploy the web application just copy the „.war“ file from the „target“ directory to Apache – Tomcat “webapps” folder

To lunch the application point your browser to the following address

http://localhost:8080/{application_name}/

If all went well you should see your main web page. Two text boxes should be displayed followed by a button each. 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.


You can download the project from here


Hope you liked it


Justin


Related Articles :

Related Snippets :

38 comments:

  1. Love it! (both the first and second version)
    Thanks for sharing!

    ReplyDelete
  2. Not too bad, but...

    The spring4gwt is outdated (the most recent download is dated July, 2009 which is at most GWT 1.7 with current 2.0.3 and 2.1 already available).

    Why not use gwt-sl (a part of the http://gwt-widget.sourceforge.net/ project) instead? It's Spring 3.0.0, Gilead (for Hibernate entities passing to GWT), GWT 2.0.

    Not using UIBinder is sooo pre-2.0 (not to mention 2.1 MVP out-of-the-box support, but even here you could use MVP pattern as well).

    Why not mention JUnit-ability of GWT as well?

    ReplyDelete
  3. Hi,
    I would agree with the comment about spring4gwt being a bit dated. Here is an example of using gwt-sl:
    http://nayidisha.com/techblog/using-gwt-in-an-enterprise-application

    I've tried to keep the technologies simple by not having too many moving parts like Hibernate thrown in.
    Here's the approach:
    1. Set up the Eclipse environment
    2. GWT app that only draws out some HTML objects but uses Maven
    3. GWT app that makes a call to a GWT Servlet using RPC
    4. GWT app that makes a call to a plain Servlet using the HTTP package
    5. GWT app that makes a call to a Spring service using RPC
    6. GWT app that makes a call to a Spring service using the HTTP package

    ReplyDelete
  4. Good article, but some steps are missing (at least it was my case): the JBoss repository must be explicitly included in order to download the org.hibernate dependencies. Although "hibernate-core" keeps being missed (don't know why), the other dependencies where downloaded and solved.

    This is what I did:

    1. In the Eclipse menu, go to Window > Preferences

    2. Go to the Maven section

    3. Select the POM Editor Option

    4. Check the "Show advanced tabs in the POM editor" option

    5. Apply changes, go to the Repositories tab in the POM editor

    6. Add the folowing repository:
    Id: jboss, Name: JBoss repository, Url: http://repository.jboss.org/maven2

    7. Save your POM

    8. Eat healthy food, and enjoy life (optional step)

    Thanks for this tutorial :)

    ReplyDelete
  5. José Francisco, you are absolutely right!!

    Maven had already downloaded them for another project i am working on, and it did not complain for this one!

    I will revise the tutorial and the project that you can download.

    Sorry for the inconvenience

    BRs

    Justin

    ReplyDelete
  6. Following the tutorial I found something:

    There's a dependency that is neither in the tutorial nor the project you made available to download: in the Application class, there's a reference to com.javacodegeeks.gwtspring.shared.services.EmployeeServiceAsync, which you describe as a proxy to access the Employee service, but there's no such a class created in the project.

    Is there something missing, or do my eyes deceive me? ;)

    Greetings,

    ReplyDelete
  7. Hello José,

    The proxy class is automatically created by Maven, so you do not have to bother creating a trivial interface! Thats the reason I don not mention it.

    Eclipse complains because the Java class is not in the source classpath, but Maven build is performed successfully.

    If you like you could add the /target/generated-sources/gwt as a source folder for your project (after you build your project with Maven once) and Eclipse will not bother you again!

    BRs

    Justin

    ReplyDelete
  8. I was a big fan of spring4gwt. The fact that it wasn't being maintained made it too risky to use on my projects. I also used GWT-SL, and it worked described.

    You may be interested in checking out this blog post on generating GWT applications based on MVP pattern/architecture and integrated with Spring back-end.
    http://www.genuitec.com/blog/?p=1604

    Regards,
    Niel

    ReplyDelete
  9. Hello Justin,

    I've just followed the tutorial step by step, and when I use the "mvn gwt:run" goal everything seems ok (at least the app starts and the browser displays it).

    The thing is that gwt:run runs the application on hosted mode, so when I try to deploy it in a Tomcat and run it 'normally', I get a strange error:

    java.lang.IncompatibleClassChangeError: Implementing class

    .. and as far as I can see.. it has something to be with the applicationContext.xml and "the definition of the bean named 'entityManagerFactory'".

    Any idea?

    Apart from that.. great tutorial ;)

    ReplyDelete
  10. This was really brilliant!

    ReplyDelete
  11. Hello Borja,

    Please check what version of JVM you set for the Maven compiler to use. You should change the "maven.compiler.source" and "maven.compiler.target" variables according to your runtime environment.

    Maybe your Eclipse project is configured to use a different JVM than your Tomcat instance!

    BRs

    Justin

    ReplyDelete
  12. java version "1.6.0_14"
    Java(TM) SE Runtime Environment (build 1.6.0_14-b08)

    ... and in my pom.xml

    maven.compiler.source and maven.compiler.target are 1.6, and Tomcat is using that JVM, obviously.

    So it looks like that's not the problem. I'll keep on searching. Thanks anyway!

    ReplyDelete
  13. Hello again,

    I guess I found the key... and I guess it was my fault.

    I've managed to run the application with Tomcat, and the main problem was that there were jars of different versions of Spring and Hibernate. Perhaps something to do with the way maven manages dependencies, as in my IDE project only the last version jars appear.

    At least now I'm a step further :D

    ReplyDelete
  14. When I ran it, I got "Error creating bean with name 'greetingService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.greeting.web.gwt.domain.EmployeeDaoImpl com.greeting.service.GreetingServiceImpl.empDao; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'employeeDao' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Cannot resolve reference to bean 'entityManagerFactory' while setting bean property 'entityManagerFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: java.net.InetAddress is a restricted class. Please see the Google App Engine developer's guide for more details."

    Please help resolving the issue.

    Regards,
    C. Patra

    ReplyDelete
  15. Hello,

    If I run 'mvn gwt:compile' on your project I get an error:
    [ERROR] Line 39: No source code is available for type com.javacodegeeks.gwtspring.shared.services.EmployeeServiceAsync; did you forget to inherit a required module?

    Also, when I run 'mvn gwt:run' I get a 404 error after hitting the save button.

    I have tried debugging it, but cant work it out!

    ReplyDelete
  16. Actually i've found the project does work on Tomcat.
    It was just having trouble running with the gwt:run command using Jetty.

    Anyway, thanks for the great tutorial.

    ReplyDelete
  17. Great Article! It helped me a lot to quickly rump up into the GWT technology.

    I have tried to run/debug the example with Eclipse GWT plugin. Unfortunately there is problem with maven gwt plugin.
    Not all sources are being copied to the /war directory while running gwt:compile (copies only compiled classes) or gwt:run (copies only dependencies defined in pom.xml).

    Ultimately, jetty's war/WEB-INF dir does not contains the following files:
    -lib/spring4gwt-0.0.1.jar
    -applicationContext.xml
    -web.xml

    Finally, if you copy those 3 files manually to the /war directory the example will work smoothly in hosted mode (using both eclipse and maven plugin). Debugging should work fine as well.

    Is there any other solution, to this issue?

    ReplyDelete
  18. For people not able to locate the "Asnc".java
    run "mvn gwt:generateAsync" and the file is generated in your target/generated-sources folder. This path can be modified @see:
    http://mojo.codehaus.org/gwt-maven-plugin/generateAsync-mojo.html

    ReplyDelete
  19. Hello Justin,
    first of all I want to thank you for this sample app. It helped me a lot to start with GWT and Spring+Hibernate.
    I wanted to play a bit with your app and change it so that it does not (directly) use JPA anymore. I usually don't create a separate persistence config xml file but configure it in spring configuration file (applicationContext.xml). However, all the sample GWT+Spring+Hibernate applications I found on the Internet do it the way you did. Why? Is it impossible to "merge" persistence config into spring config or is it some kind of good practice to leave it separated?
    Regards
    Robert

    ReplyDelete
  20. Hello Berto,

    If you want to utilize JPA over a persistence provider (such as Hibernate) then you have to have a persistence config xml file. The persistence.xml file is JPA specific file, not Hibernate requirement.

    So you can skip the JPA layer entirely and go with Spring and Hibernate alone so all configuration can be made from your applicationContext.xml file. There is plenty of information out there about integrating Spring with Hibernate, so give it a try!

    Hope I helped!

    BRs

    ReplyDelete
  21. FYI: I could not find the gwt-maven-plugin in the Nexus Indexer

    I clicked 'Add Archetype...' and entered:
    * Archetype Group Id: org.codehaus.mojo
    * Archetype Artifact Id: gwt-maven-plugin
    * Archetype Version: 2.1.0-1
    * Repository URL: http://repo1.maven.org/maven2
    then it worked

    ReplyDelete
  22. I tried to launch the application on a tomcat server (v6.0) and I've got this warning :

    log4j:WARN No appenders could be found for logger (org.springframework.web.context.ContextLoader).
    log4j:WARN Please initialize the log4j system properly.

    Do you know why?

    ReplyDelete
  23. Hello edouard,

    You should place a log4j.properties file on your runtime classpath. But this is not a blocking factor for working with the application since it is a WARN level log.

    BRs

    ReplyDelete
  24. Hello Justin

    I have a problem with Hibernate ....
    If i use Lazy parameter in an entity I have Exception.
    Do you know how can i use gilead to solve it ?

    I read a lot of forum but all tutorial works ...

    Can you help me please ?

    ReplyDelete
  25. Hello cyril,

    In the following days we will post an updated version of this tutorial which will make use of the Gilead and gwt-sl libraries so as to overcome this kind of issues, so stay tuned!

    Regards

    ReplyDelete
  26. Thx for nice tutorial. But my eyes and heart ache looking at this bloated code required to do simple stuff. There is something very wrong in writing html and javascript in Java

    ReplyDelete
  27. Hi von-g,

    Thanks for your support. Yeah, you have a valid point. It is indeed a little weird to mix HTML Javascript and Java, but overall GWT provides a quite robust and efficient way to produce Ajax application.

    Cheers,
    Ilias

    ReplyDelete
  28. I was wondering if you have any plans to write an article on : GWT MVP (Activities, Places, etc.) + Spring.

    Thanks for all your work on this blog. It is really well done.

    ReplyDelete
  29. I think that if you add hibernate-entitymanager dependency you won't be able to use the jpa 2 full functionality.

    ReplyDelete
  30. hi.
    I imported the project in my eclipse, ran mvn install, gwt:compile and gwt:run. everything was ok so far, but when i open the site, and try to save an employee i get An error occurred while attempting to contact the server. Please check your network connection and try again. The error is : com.google.gwt.user.client.rpc.StatusCodeException: HTTP ERROR: 404 NOT_FOUND RequestURI=/com.javacodegeeks.gwtspring.Application/springGwtServices/employeeService. please can aanyone help me?

    ReplyDelete
  31. This will sound like a completly rookie question, but I've been searching for this for ages.

    Using m2eclipse

    I've tried adding the advanced tabs to my POM by going to preferences-maven, but I don't have any option relating to an Editor there, the closest I managed was "User Interface" which has "Open XML page in the POM editor by default"..so there's no way for me to add the advanced tabs.

    Has anyone else run into this problem, and if no one has is there a way for me to add the repository manually in the POM without the need for the advanced tabs?

    ReplyDelete
  32. you have example using multiple database in SQL server 2008 R2 and example of relationship between database ?
    Thanks

    ReplyDelete
  33. Amazing article,i got the grip of how all these can work together.

    I have an requirement where i have to integrate the Spring security with gwt. i tried and went through this tutorial

    http://www.javacodegeeks.com/2010/12/securing-gwt-apps-with-spring-security.html

    but this has a limitation where i have to depend on the default login page.Another alternative is by using a custom jsp page,but i am looking forward for a plain GWT login scenario without the interference of jsp .I will be grateful to you for any help or suggestions .

    ReplyDelete
  34. Guy Armitage, I think this is because you're using Eclipse Indigo. Try adding the repository info in the pom.xml:

    <repositories>
    <repository>
    <id>jboss</id>
    <url>http://repository.jboss.org/maven2/</url>
    </repository>
    </repositories>

    ReplyDelete
  35. trying to deploy this to JBOSS 7.0.2 resulted in a funny class-cast exception. probably something to do with JBOSS and not the example though...

    12:34:57,718 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-1) MSC00001: Failed to start s
    ervice jboss.persistenceunit."gwtspring-0.0.1-SNAPSHOT.war#MyPersistenceUnit": org.jboss.msc.service
    .StartException in service jboss.persistenceunit."gwtspring-0.0.1-SNAPSHOT.war#MyPersistenceUnit": F
    ailed to start service
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1780
    )
    ......

    Still would appreciate pointers.
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) [:1.6
    .0_17]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) [:1.6.0_1
    7]
    at java.lang.Thread.run(Thread.java:619) [:1.6.0_17]
    Caused by: java.lang.ClassCastException: org.dom4j.DocumentFactory cannot be cast to org.dom4j.Docum
    entFactory
    at org.dom4j.DocumentFactory.getInstance(DocumentFactory.java:97)
    at org.dom4j.DocumentHelper.getDocumentFactory(DocumentHelper.java:36)

    After review, I've found the JBOSS AS7 needs additional configuration:
    ->configure persistance unit
    https://docs.jboss.org/author/display/AS7/JPA+Reference+Guide


    still ultimately I still get:
    java.lang.NoClassDefFoundError: org/jboss/virtual/VirtualFileVisitor

    so I guess additional changes to the pom file need to be done.

    ReplyDelete
  36. I'm using spring4Gwt with GXT but I have a problem with resources like icons. org.spring4gwt.server.SpringGwtRemoteServiceServlet is not able to handle the request for the resources becuase it is only trying to load beans. is there any ideas about this issue??

    ReplyDelete
  37. go to 'Source' tab and add the repository configuration:

        
         
              JBoss
              http://repository.jboss.org/maven2/
         
       

    between '' and ''.

    When you have done it, go to 'Effective POM' and you will see the repository added at your configuration.

    ReplyDelete
  38. Good article, but is outdate.

    I have tried to make it work with Eclipse Indigo + GWT 2.4, but it doesn't work.

    Are you going to update the article?

    Thanks.

    ReplyDelete

Related Posts Plugin for WordPress, Blogger...