Announcement Announcement Module
Collapse
No announcement yet.
Step by step tutorial to Jee Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Step by step tutorial to Jee

    This solution is outadated.
    SpringSource and Google teams have worked together and produced amazing results, as usal.
    Using STS (SpringsourceToolSuite) with spring roo and GWT plugin, you can get everything I describe here work in 10 minutes (have a look at http://www.youtube.com/watch?v=a46hJYtsP-8 from around 1h20).


    Trying to set up a proof of concept for porting the software I am working on from MFC / VS2005 to GWT / Spring / Hibernate / Maven2 project with Eclipse, I spent a lot of time choosing parts and putting them together. Many good tutorials for complete beginners are available for each technology (GWT, Spring MVC, …).
    But when it comes up to have GWT client connected to a Spring server and a build process controlled by maven, tutorial I found where hardly accessible to newbies. So here is a guide from scratch to top of the hedge technology!
    You won't find here all info you need to understand in detail any of the technology I mentioned. An article wouldn't be enough. Actually it would take several books.
    My aim is just to produce a cook-book to get a base project with all the technology you may need to achieve user friendly multi-tier application that scales well.
    All given download links and configuration instructions are given for Windows. If you use an other OS, you will have to adapt on your own.

    Before everything
    As I wrote, I'm quite new to jee, so I couldn't have achieved this if I hadn't copied on others. Here are the link to the articles I took the key parts of this tutorial from:So I would like to thank Stephen Callaghan and Chris Lee for the time they spent writing this, it was of great help.

    Contents
    As I have quite e few things to tell, I will split the content as follow :
    • Get parts up and ready
    • Abstract
    • Gwt-spring project
    • tuto-dto project
    • tuto-client project
    • tuto-server project
    • Improve tuto-server by connecting to a database with hibernate
    • Improve tuto-client using gwt-ext widget library

    Further readings
    Last edited by ch4mp; Sep 22nd, 2010, 05:25 AM. Reason: add Google Web Toolkit keyword (GWT is too short for search feature)

  • #2
    Get parts up and ready

    Downloads
    First thing to do is to download required software:
    System setup

    Install Java under C:\dev\java\jdk1.6.0_12_x86 (adapt the path to java version, it changes quite often) and unpack Eclipse in C:\dev\eclipse.
    Add those environment variables:
    • JAVA_HOME=C:\dev\java\jdk1.6.0_12_x86
    • JRE_HOME=%JAVA_HOME%\jre
    Also add %JRE_HOME%\bin;%JAVA_HOME%\bin; at the beginning of PATH variable.
    Unpack mysql-connector-java-5.1.7.zip to C:\dev

    XAMPP setup

    Unpack xampp-win32-1.7.0.zip in C:\dev and xampp-patch2-win32-1.7.0.zip in C:\dev\xampp.
    Run C:\dev\xampp\setup_xampp.bat, C:\dev\xampp\xampp-control.exe. Check Svc (install service) for both Apache and MySQL. Start Apache and MySQL.
    Open a web browser and go to http://localhost. Choose a language and then browse to http://localhost/security/xamppsecurity.php (if you can't, edit C:\dev\xampp\apache\conf\extra\httpd-xampp.conf and replace Allow from localhost with Allow from 127.0.0.1 in Directory "C:/dev/xampp/security/htdocs" section).
    Go to http://localhost/phpmyadmin/, log in as root using your freshly set password, move to users tab (icon with a person and a lock) and click add user link. Set tuto as user name, localhost as host, something as password (twice) and choose Create a database with the same name and grant all privileges. Click Execute.

    Eclipse setup
    Start Eclipse and choose C:\dev\workspace\tuto as workspace (I recomand you do not check “use this as default...”). I use eclipse workspaces as Visual Studio solutions. tuto is the workspace we will be using for this whole tutorial, but we could have had one dedicated to house-build cross project support frameworks (gwt-spring project). You should choose an other workspace for your own projects.
    Check that eclipse is aware of your JDK: in Window->Preferences->Java->Installed JREs C:\dev\Java\jdk1.6.0_12_x86 should be present. If not add it.
    If you are behind a proxy, you need to set it up both in eclipse (Window->Preferences->General->Network connections) and in maven local settings (create a .m2 directory in your home directory and a settings.xml file in this new directory). See http://maven.apache.org/guides/mini/guide-proxies.html for more details.
    Next, we need to get additional plugins for Eclipse: in menu Help->Software and updates...->Available Software add those sites:
    Install those five plugins. Each time you add a location, the content is expended. Select all that is expended and that can be installed (I got tired to try minimal subset).
    Open Window->Open Perspective->Other...->Database Development. Right click Databases in data source explorer and choose New.... Simple click MySQL and input mysql_tuto as name. Click Next > and then New Driver Definition (top right). Select MySQL 5.1 then move to Jar List tab, select the entry you have there and click Edit JAR/ZIP.... Select the jar file in C:\dev\mysql-connector-java-5.1.7 (the one we have downloaded from MySQL). Click Open and OK to return to new connection profile window. Input tuto as database, jdbc:mysql://localhost:3306/tuto as URL, tuto as user name and something as password. Click Finish and reopen Java EE perspective.
    Last edited by ch4mp; Apr 11th, 2009, 03:29 AM. Reason: Add AJDT and JBoss tools plugins

    Comment


    • #3
      Abstract

      To keep things simple, we will use a very basic business model for the examples and discuss in the end how to scale it to real life. For now the entire business is reduced to currencies and the only info we are interested in is the three characters ISO code.
      Here is the architecture overview:
      • The currencies are stored in a relational database (MySQL)
      • User display is made in a web browser using HTML & JavaScript (GWT)
      • A Service is running on an application server (Spring MVC servlet running on Tomcat) to catch client requests and process business logic (for now reduced to database querying with the support of Hibernate)
      To achieve this we will set up a few projects:
      • gwt-spring: a small jar with what is necessary to integrate GWT servlet in Spring (a custom HandlerAdapter, a custom UrlHandlerMapping and an annotation to tag GWT services)
      • tuto-dto: ajar containing very basic java pojos used by GWT to transfer data across network. It is important that this jar is deployed with sources.
      • tuto-server: a webapp hosting services running in Spring context.
      • tuto-client: a GWT client consuming services embedded in server project
      Why GWT ?
      The Google Web Toolkit is intended to facilitate not so light client coding to Java developers. Here are few points which have influenced my choice:
      • All coding is made in Java (client side is compiled to JavaScript with a tool provided by Google).
      • GUI design is very similar to Swing.
      • It's easy to find widget libraries with ready to use functionalities such as date pickers, dynamic tables (switchable columns, sortable lines, …), menus and so on.
      • All serialization plumbing for client/server communication is transparently done by GWT.
      • The ratio time spent coding / visual rendering is competitive even with only little learning.
      Why Spring ?
      The first thing is I like the way Spring guides developers to best practices. Using Spring, it is frequently harder to make things dirty.
      The second is the ease to use standard frameworks such as log4j or hibernate without being too dependent on them (should be relatively easy to switch to something else).
      At last, Spring is well documented and widely used. So it's easy to find help and the technology shouldn't be deprecated soon.

      Why Maven2 ?
      Using Maven, it's easy (OK, not so for newbies, but it's much harder without it) to solve dependencies conflicts, automate builds and tests, integrate with code quality tools, standardize projects layouts and so on.
      Actually, Maven is a requirement from quality control teams for all Java projects in an increasing number of companies.

      Why MySQL, Tomcat and Eclipse ?
      Why not ?
      More seriously, the three are freely available, efficient, well documented easy to set-up and widely used. Moreover, using Xampp, you can have an integration infrastructure (MySQL & Tomcat) up and running in a few clicks. You even get a web server to publish documentation across your team.

      Why are unit tests invading this tutorial ?
      Well, try once to have automated tests coded at the same time as your application. You should quickly notice this:
      • Initial overhead is minimal within eclipse.
      • Thinking to what you will test help you understand the requirements. Quite often, unit test investment is therefore paid back before the functionality is fully implemented.
      • Writing tests with users points out early in development life cycle the requirements defects. So you will spent minimal time coding useless or buggy functionalities.
      • 9 out of 10 times, refactoring is a piece of cake: side effects are painted in red and broken functionalities are pointed out. So code keeps clean when requirements evolve (they always do unless nobody cares about what your are coding).
      • Keeping tests up to date is easy, all you have to do is point out declared bugs with unit tests before correcting them.
      • Unit test are my favorite way to document requirements (what is the software ought to do and what are the anticipated special cases).
      Not convinced yet ? So maybe you should read this paragraph again.
      Last edited by ch4mp; Mar 8th, 2009, 01:04 PM.

      Comment


      • #4
        Gwt-spring project

        This part is directly taken from Chris Lee blog.

        Project setup
        1. Create a File->new->project...->Maven->Maven Project. Check Create a simple project and click next
        2. enter:
          • group id: fr.free.hd.ch4mp.support
          • artifact id: gwt-spring
          • version: 1.0
          • Packaging: jar
          • Name: gwt-spring
          • Description: Support classes to integrate GWT servlet in Spring
        3. click Finish
        4. In project explorer, right click gwt-spring project. In Properties->Java Compiler uncheck Enable project specific settings.
        Maven setup
        Open pom.xml (project root) then click Dependencies tab and then Add dependency icon on top left. Enter gwt-user and double click on the result. Repeat this with spring-webmvc (org.springframework).

        Java code
        GwtHandlerAdapter
        1. In project explorer right click src/main/java source directory and choose new->class. Enter fr.free.hd.ch4mp.support.gwt as package and GwtHandlerAdapter as name.
        2. In superclass section, click Browse... button, enter RemoteServiceServlet and double click the result.
        3. In interface section, click Add... button, enter HandlerAdapter, click the result and click Add, then input ApplicationContextAware, click the result and click OK.
        4. Click Finish
        PHP Code:
        package fr.free.hd.ch4mp.support.gwt;


        import javax.servlet.ServletContext;
        import javax.servlet.http.HttpServletRequest;
        import javax.servlet.http.HttpServletResponse;


        import org.springframework.beans.BeansException;
        import org.springframework.context.ApplicationContext;
        import org.springframework.context.ApplicationContextAware;
        import org.springframework.web.context.WebApplicationContext;
        import org.springframework.web.servlet.HandlerAdapter;
        import org.springframework.web.servlet.ModelAndView;


        import com.google.gwt.user.client.rpc.RemoteService;
        import com.google.gwt.user.client.rpc.SerializationException;
        import com.google.gwt.user.server.rpc.RPC;
        import com.google.gwt.user.server.rpc.RPCRequest;
        import com.google.gwt.user.server.rpc.RemoteServiceServlet;


        public class 
        GwtHandlerAdapter extends RemoteServiceServlet implements HandlerAdapterApplicationContextAware {
            private static final 
        long serialVersionUID 3690031757210388658L;
            private static 
        ThreadLocal<ObjectperThreadHandler = new ThreadLocal<Object>();
            private 
        ApplicationContext applicationContext;
            
            @
        Override
            
        public long getLastModified(HttpServletRequest arg0Object arg1) {
                return -
        1;
            }


            @
        Override
            
        public ModelAndView handle(HttpServletRequest requestHttpServletResponse responseObject handlerthrows Exception {
                try {
                    
        // store the handler for retrieval in processCall()
                    
        perThreadHandler.set(handler);
                    
        doPost(requestresponse);
                } 
        finally {
                    
        // clear out thread local to avoid resource leak
                    
        perThreadHandler.set(null);
                }
               return 
        null;
            }


           @
        Override
            
        public boolean supports(Object handler) {
                return 
        handler instanceof RemoteService;
            }


            protected 
        Object getCurrentHandler() {
                return 
        perThreadHandler.get();
            }


            @
        Override
            
        public String processCall(String payloadthrows SerializationException {
                try {
                    
        RPCRequest rpcRequest RPC.decodeRequest(payloadgetCurrentHandler().getClass());
                    return 
        RPC.invokeAndEncodeResponse(getCurrentHandler(), rpcRequest.getMethod(), rpcRequest.getParameters());
                } catch (
        Throwable t) {
                    return 
        RPC.encodeResponseForFailure(nullt);
                }
            }


            @
        Override
            
        public ServletContext getServletContext() {
                return ((
        WebApplicationContext)applicationContext).getServletContext();
           }


            @
        Override
            
        public void setApplicationContext(final ApplicationContext applicationContextthrows BeansException {
                
        this.applicationContext applicationContext;
            }

        GwtRpcEndPoint

        In project explorer right click fr.free.hd.ch4mp.support.gwt package and choose new->Annotation. Enter GwtRpcEndPoint as name and click finish.
        PHP Code:
        package fr.free.hd.ch4mp.support.gwt;


        import java.lang.annotation.ElementType;
        import java.lang.annotation.Retention;
        import java.lang.annotation.RetentionPolicy;
        import java.lang.annotation.Target;


        @
        Target({ElementType.TYPE })
        @
        Retention(RetentionPolicy.RUNTIME)
        public @interface 
        GwtRpcEndPoint {
            
        String value() default "";

        GwtUrlHandlerMapping

        In project explorer right click fr.free.hd.ch4mp.support.gwt package and choose new->class. Enter fr.free.hd.ch4mp.support.gwt as package and GwtUrlHandlerMapping as name. Set AbstractDetectingUrlHandlerMapping as superclass.
        PHP Code:
        package fr.free.hd.ch4mp.support.gwt;


        import org.springframework.util.StringUtils;
        import org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping;


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


        /**
         * @author Ch4mp
         * Does auto URL mapping for services flagged with the RemoteService interface
         */
        public class GwtUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
           private String prefix="";
            private String suffix="";
            
            protected String[] buildUrls(Class<?> handlerType, String beanName) {


               String remoteServiceName = null;
                Class<?>[] interfaces = handlerType.getInterfaces();
                for (Class<?> itf : interfaces) {
                    // find the interface that extends RemoteService
                    if (RemoteService.class.isAssignableFrom(itf)) {
                        remoteServiceName = itf.getName();
                   }
                }


                if (remoteServiceName == null) {
                    throw new IllegalArgumentException("Unable to generate name for " + handlerType.getName()
                            + "; cannot locate interface that is a subclass of RemoteService");
                }
               String classPath = StringUtils.replace(remoteServiceName, ".", "/");


               StringBuilder sb = new StringBuilder();


               sb.append(prefix);


                sb.append(classPath);


                sb.append(suffix);
                return new String[] { sb.toString() };
            }


            @Override
            protected final String[] determineUrlsForHandler(String beanName) {
                String[] urls = new String[0];
                Class<?> handlerType = getApplicationContext().getType(beanName);
                if (handlerType.isAnnotationPresent(GwtRpcEndPoint.class)) {
                   GwtRpcEndPoint endPointAnnotation = handlerType.getAnnotation(GwtRpcEndPoint.class);
                    if (StringUtils.hasText(endPointAnnotation.value())) {
                        urls = new String[] { endPointAnnotation.value() };
                    } else {
                        urls = buildUrls(handlerType, beanName);
                    }
                }


                return urls;
            }


            public void setPrefix(String prefix) {
                this.prefix = prefix;
            }


            public void setSuffix(String suffix) {
                this.suffix = suffix;
            }


        }
        Build and deploy jar with sources
        In project explorer right click gwt-spring project and click Run as->Maven Install. Yes, that's all. Nice isn't it ?

        Comment


        • #5
          tuto-dto part1

          Create a new simple Maven project with following characteristics: fr.free.hd.ch4mp.tuto as group id, tuto-dto as artifact id, jar as packaging, tuto-dto as name, 1.0 as version and Data transfer objects and public service interfaces as description.
          Project setup

          In project explorer, right click tuto-dto project. In Properties->Java Compiler uncheck Enable project specific settings and set them to1.6.
          Open common/pom.xml to add gwt-user dependency and maven-source-plugin plugin the same way you may have done for gwt-spring project. This time, the attach-source execution is absolutely necessary because of GWT which needs sources to generate JavaScript code.

          Business classes
          We are going to define our business classes so that it can be used as GWT DTOs. Data Transfer Objects are used to transfer data (what a surprise!) across serialization. GWT is able to automatically deal with object transmission over network and between Java and JavaScript. The only requirement is implementing IsSerializable interface and be sure that other used objects do.
          In src/main/java source directory, add two classes in fr.free.hd.ch4mp.tuto.business.currency package: Entity and Currency. Entity is an abstract class that will be used as basis for every class that also is a database entity. Do I need to explain what a Currency is ? Maybe not, but let's have a look at it's model. We are interested in three characteristics: the ISO code on three characters, the number of decimals and the rounding method. Most often monetary amounts are expressed with two decimals and rounding to the closer, but JPY (Japan Yen) for instance has no decimal and amounts are truncated (1.99 => 1).
          PHP Code:
          package fr.free.hd.ch4mp.tuto.business.currency;

          import com.google.gwt.user.client.rpc.IsSerializable;

          public 
          enum RoundingMethod implements IsSerializable {
              
          CLOSER,
              
          TRUNCATE;
              
              
          double round(double valueint precision){
                  
          Double tmp = new Double(value Math.pow(10precision));
                  switch(
          this){
                  case 
          TRUNCATE:
                      if(
          tmp >= 0)
                          return 
          Math.floor(tmp) * Math.pow(10, -1*precision);
                      return 
          Math.ceil(tmp) * Math.pow(10, -1*precision);
                  default:
                      return 
          Math.round(tmp) * Math.pow(10, -1*precision);
                  }
              }
              
              
          Double round(Double valueInteger precision){
                  if(
          value == null || precision == null)
                      return 
          null;
                  return new 
          Double(round(value.doubleValue(), precision.intValue()));
              }

          PHP Code:
          package fr.free.hd.ch4mp.tuto.business;

          import com.google.gwt.user.client.rpc.IsSerializable;

          /**
           * @author Ch4mp
           * Base class for whatever can be stored as a database entity identified with an auto incremented integer
           */
          public abstract class Entity implements IsSerializable{
              
          /**
               * Numeric primary key in the database
               */
              
          private Long id;

              public 
          Entity() {
                  
          this.id null;
              }

              public 
          Entity(Long id) {
                  
          this.id id;
              }

              
          /**
               * @return Entity numeric primary key in the database (can be null)
               */
              
          public Long getId() {
                  return 
          id;
              }

              public 
          void setId(Long id) {
                  
          this.id id;
              }

          PHP Code:
          package fr.free.hd.ch4mp.tuto.business.currency;

          import com.google.gwt.user.client.rpc.IsSerializable;

          import fr.free.hd.ch4mp.tuto.business.Entity;

          /**
           * @author Ch4mp
           * Currency Data Transfer Object used across serialization
           */
          public class Currency extends Entity implements IsSerializable {
              
          /**
               * Currency ISO code (three upper case chars)
               */
              
          private String isoCode;
              
          /**
               * Number of decimals for monetary amounts in this currency
               */
              
          private Integer decimals;
              
          /**
               * Rounding method for currency amounts
               */
              
          private RoundingMethod roundingMethod;
              
              public 
          Currency() {
                  
          super();
                  
          isoCode null;
                  
          decimals null;
                  
          roundingMethod null;
              }
              public 
          Currency(String isoCodeInteger decimalsRoundingMethod roundingMethodthrows Exception {
                  
          super();
                  
          setIsoCode(isoCode);
                  
          setDecimals(decimals);
                  
          setRoundingMethod(roundingMethod);
              }
              public 
          Currency(Long idString isoCodeInteger decimalsRoundingMethod roundingMethodthrows Exception {
                  
          super(id);
                  
          setIsoCode(isoCode);
                  
          setDecimals(decimals);
                  
          setRoundingMethod(roundingMethod);
              }
              
              @
          Override
              
          public boolean equals(Object other){
                  if(
          other == null)
                      return 
          false;
                  
                  if(!(
          other instanceof Currency))
                      return 
          false;
                  
                  if(
          isoCode==null || decimals==null || roundingMethod==null)
                      return 
          false;

                  
          Currency tmp = (Currency)other;
                  return 
          isoCode.equals(tmp.isoCode)
                      && 
          decimals.equals(tmp.decimals)
                      && 
          roundingMethod == tmp.roundingMethod;
              }
              
              public 
          double round(double amountthrows Exception {
                  if(
          decimals == null)
                      throw new 
          Exception("Decimal number is not set");
                  if(
          roundingMethod == null)
                      throw new 
          Exception("Rounding method is not set");
                  return 
          roundingMethod.round(amountdecimals.intValue());
              }
              
              public 
          Double round(Double amountthrows Exception {
                  if(
          amount == null)
                      return 
          null;
                  return new 
          Double(round(amount.doubleValue()));
              }

              public 
          String getIsoCode() {
                  return 
          isoCode;
              }
              public 
          void setIsoCode(String isoCodethrows Exception {
                  if(
          isoCode == null || isoCode.length() != 3)
                      throw new 
          Exception("Currency ISO code must be 3 char long");
                  
          this.isoCode isoCode.toUpperCase();
              }
              
              public 
          Integer getDecimals() {
                  return 
          decimals;
              }
              public 
          void setDecimals(Integer decimalsthrows Exception {
                  if(
          decimals == null || decimals.intValue() < 0)
                      throw new 
          Exception("Decimal number must be positive");
                  
          this.decimals decimals;
              }
              
              public 
          RoundingMethod getRoundingMethod() {
                  return 
          roundingMethod;
              }
              public 
          void setRoundingMethod(RoundingMethod roundingMethodthrows Exception {
                  if(
          roundingMethod == null)
                      throw new 
          Exception("Rounding method can't be null");
                  
          this.roundingMethod roundingMethod;
              }

          Comment


          • #6
            tuto-dto part2

            JUnit
            First, we need too declare a dependency on JUnit in pom.xml. Open tuto-dto/pom.xml, then open Dependencies tab and search for junit:junit. Set test as scope and save.
            Right click RoundingMethod class and choose new->Other.... Then filter with junit and double click JUnit Test Case.
            Change Source Folder from tuto-dto/src/main/java to tuto-dto/src/test/java and click Finish.
            PHP Code:
            package fr.free.hd.ch4mp.tuto.business.currency;

            import static org.junit.Assert.assertEquals;

            import org.junit.Before;
            import org.junit.Test;

            public class 
            RoundingMethodTest {
                private 
            RoundingMethod closer;
                private 
            RoundingMethod truncate;
                
                @
            Before
                
            public void setUp() throws Exception {
                    
            closer RoundingMethod.CLOSER;
                    
            truncate RoundingMethod.TRUNCATE;
                }
                
                @
            Test
                
            public void testRound(){
                    
            assertEquals(0.0closer.round(0.00), Math.pow(10, -6));
                    
            assertEquals(0.0truncate.round(0.00), Math.pow(10, -6));
                    
            assertEquals(0.0closer.round(0.30), Math.pow(10, -6));
                    
            assertEquals(0.0truncate.round(0.30), Math.pow(10, -6));
                    
            assertEquals(1.0closer.round(0.50), Math.pow(10, -6));
                    
            assertEquals(0.0truncate.round(0.50), Math.pow(10, -6));
                    
            assertEquals(1.0closer.round(0.90), Math.pow(10, -6));
                    
            assertEquals(0.0truncate.round(0.90), Math.pow(10, -6));
                    
            assertEquals(0.0closer.round(-0.50), Math.pow(10, -6));
                    
            assertEquals(0.0truncate.round(-0.50), Math.pow(10, -6));
                    
            assertEquals(-1.0closer.round(-0.90), Math.pow(10, -6));
                    
            assertEquals(0.0truncate.round(-0.90), Math.pow(10, -6));
                    
            assertEquals(1.6closer.round(1.551), Math.pow(10, -6));
                    
            assertEquals(1.5truncate.round(1.551), Math.pow(10, -6));
                }
                
                @
            Test
                
            public void testRoundObjects(){
                    
            assertEquals(new Double(0.0), closer.round(new Double(0.0), new Integer(0)), Math.pow(10, -6));
                    
            assertEquals(new Double(0.0), truncate.round(new Double(0.0), new Integer(0)), Math.pow(10, -6));
                    
            assertEquals(new Double(0.0), closer.round(new Double(0.3),  new Integer(0)), Math.pow(10, -6));
                    
            assertEquals(new Double(0.0), truncate.round(new Double(0.3),  new Integer(0)), Math.pow(10, -6));
                    
            assertEquals(new Double(1.0), closer.round(new Double(0.5),  new Integer(0)), Math.pow(10, -6));
                    
            assertEquals(new Double(0.0), truncate.round(new Double(0.5),  new Integer(0)), Math.pow(10, -6));
                    
            assertEquals(new Double(1.0), closer.round(new Double(0.9),  new Integer(0)), Math.pow(10, -6));
                    
            assertEquals(new Double(0.0), truncate.round(new Double(0.9),  new Integer(0)), Math.pow(10, -6));
                    
            assertEquals(new Double(0.0), closer.round(new Double(-0.5),  new Integer(0)), Math.pow(10, -6));
                    
            assertEquals(new Double(0.0), truncate.round(new Double(-0.5),  new Integer(0)), Math.pow(10, -6));
                    
            assertEquals(new Double(-1.0), closer.round(new Double(-0.9),  new Integer(0)), Math.pow(10, -6));
                    
            assertEquals(new Double(0.0), truncate.round(new Double(-0.9),  new Integer(0)), Math.pow(10, -6));
                    
            assertEquals(new Double(1.6), closer.round(new Double(1.55),  new Integer(1)), Math.pow(10, -6));
                    
            assertEquals(new Double(1.5), truncate.round(new Double(1.55),  new Integer(1)), Math.pow(10, -6));
                }


            The same way you did for RoundingMethod, create a test class for Currency.
            PHP Code:
            package fr.free.hd.ch4mp.tuto.business.currency;

            import static org.junit.Assert.assertEquals;
            import static org.junit.Assert.assertFalse;
            import static org.junit.Assert.assertNull;
            import static org.junit.Assert.fail;

            import org.junit.Before;
            import org.junit.Test;

            public class 
            CurrencyTest {
                private 
            Currency currency;
                
                @
            Before
                
            public void setUp() throws Exception{
                    
            currency = new Currency("EUR");
                }
                
                @
            Test
                
            public void testGetSetIsoCode() throws Exception {
                    
            Currency currency = new Currency();
                    
            assertNull(currency.getIsoCode());
                    
            currency.setIsoCode("EUR");
                    
            assertEquals("EUR"currency.getIsoCode());
                }
                
                @
            Test
                
            public void testIsoCodeNotNull() {
                    
            Currency currency = new Currency();
                    try{
                        
            currency.setIsoCode(null);
                        
            fail("An exeption should be thrown when trying to set null ISO code");
                    }catch(
            Exception e){}
                }
                
                @
            Test
                
            public void testIsoCodeLength() {
                    
            Currency currency = new Currency();
                    try{
                        
            currency.setIsoCode("EU");
                        
            fail("An exeption should be thrown when trying to set an ISO code that is not exactly three chars long");
                    }catch(
            Exception e){}
                    try{
                        
            currency.setIsoCode("EURO");
                        
            fail("An exeption should be thrown when trying to set an ISO code that is not exactly three chars long");
                    }catch(
            Exception e){}
                }
                
                @
            Test
                
            public void testIsoCodeToUpperCase() throws Exception{
                    
            Currency currency = new Currency("eur");
                    
            assertEquals("EUR"currency.getIsoCode());
                }
                
                @
            Test
                
            public void testEquals() throws Exception{
                    
            Currency other = new Currency(new Long(-1), "EUR");
                    
            assertFalse(other == currency);
                    
            assertEquals(othercurrency);
                }
                
                @
            Test
                
            public void testNotEqualsNull() throws Exception{
                    
            assertFalse(currency.equals(null));
                }
                
                @
            Test
                
            public void testNotEqualsOtherType() throws Exception{
                    
            assertFalse(currency.equals("EUR"));
                }
                
                @
            Test
                
            public void testNotEqualsOtherNullIsoCode() throws Exception{
                    
            Currency other = new Currency();
                    
            assertFalse(currency.equals(other));
                    
            assertFalse(other.equals(currency));
                }
                
                @
            Test
                
            public void testRound() throws Exception{
                    
            assertEquals(100.61currency.round(100.609), Math.pow(10, -6));
                }

            Public service interface

            Those interfaces are used to describe the functionalities a service offers. For now we want only one service (one interface) with a single functionality (one method): retrieving all available currencies. As we want to expose it to be compatible with GWT RPC, we have to extend RemoteService interface.
            In src/main/java source directory, add a CurrencyService interface extending RemoteService in the package fr.free.hd.ch4mp.tuto.business.currency.
            PHP Code:
            package fr.free.hd.ch4mp.tuto.business.currency;

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

            public interface 
            CurrencyService extends RemoteService {
                public 
            Currency[] getAllCurrencies();

            PHP Code:
            package fr.free.hd.ch4mp.tuto.business.currency;

            import com.google.gwt.user.client.rpc.AsyncCallback;

            public interface 
            CurrencyServiceAsync {
                public 
            void getAllCurrencies(AsyncCallback<Currency[]> callback);

            Declare business package (and sub-packages) as GWT module
            Right click on fr.free.hd.ch4mp.tuto.business package and choose New->Other...->XML->XML. Remove business at the end of parent folder and name the file Business.gwt.xml.
            Code:
            <?xml version="1.0" encoding="UTF-8"?>
            <module>
            	<source path="business"/>
            </module>
            Build and deploy
            As we did for gwt-spring project, run Maven install target to deploy jar with sources to local repository. Notice that unit tests are run automatically.

            Comment


            • #7
              tuto-server part1

              The service we want is as simple as possible: retrieve a list of currencies from a persistent repository.
              Using Spring, it is highly recommended to do business logic in services and delegate persistence stuff to repositories specialized on persistence technology.
              So, we need three components:
              • an implementation for CurrencyService interface: CurrencyServiceImpl
              • as our currency service implementation does not need to know what kind of thecnology is used for persistence, we will hide the repository implementation behind an interface: CurrencyRepository
              • a currency repository dedicated to our persistence technology. For now, we will use a simplification: StubCurrencyRepository is astupid class that simply returns three hard coded currencies.An other CurrencyRepository implementation connecting to a database will be provided at a later step.
              Project setup

              Create a new maven project called tuto-server but this time leave create simple project unchecked. Uncheck Resolve Workspace projects in Advanced hidden tab. Click Next, filter with webapp and double click maven-archetype-webapp. Set fr.free.hd.ch4mp.tuto as group id, tuto-server as artifact id, 1.0 as version and fr.free.hd.ch4mp.tuto.server as package. Click Finish.
              Open project properties and uncheck Enable project specific setting in Java compiler section. Also set Java Version to 6.0 in Project Facets section.
              Open server/pom.xml and add following dependencies:
              • gwt-spring (fr.free.hd.ch4mp.common) with runtime as target
              • tuto-dto (fr.free.hd.ch4mp.tuto)
              • spring-webmvc (org.springframework)
              • spring-test (org.springframework) with test as target
              Modify junit (junit) to version 4.5
              Also add the plugin maven-jetty-plugin (org.mortbay.jetty) and maven-compiler-plugin (org.apache.maven.plugins). Then click configure. Here is plugins configuration (notice the two configuration tags):
              Code:
                      <plugins>
                          <plugin>
                              <groupId>org.mortbay.jetty</groupId>
                              <artifactId>maven-jetty-plugin</artifactId>
                              <version>6.1.14</version>
                              <configuration>
                                  <scanIntervalSeconds>10</scanIntervalSeconds>
                                  <stopKey>tuto-server</stopKey>
                                  <stopPort>9999</stopPort>
                                  <connectors>
                                      <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
                                          <port>9090</port>
                                          <maxIdleTime>60000</maxIdleTime>
                                      </connector>
                                  </connectors>
                              </configuration>
                          </plugin>
                          <plugin>
                              <groupId>org.apache.maven.plugins</groupId>
                              <artifactId>maven-compiler-plugin</artifactId>
                              <version>2.0.2</version>
                              <configuration>
                                  <source>1.6</source>
                                  <target>1.6</target>
                              </configuration>
                          </plugin>
                      </plugins>
              In project explorer, right click tuto-server project and choose New->Source Folder. Set src/main/java as folder name. Do the same to add src/test/java directory.
              Java code

              First define the CurrencyRepository interface:
              PHP Code:
              package fr.free.hd.ch4mp.tuto.business.currency;

              import java.util.List;

              public interface 
              CurrencyRepository {
                  public List<
              CurrencyretrieveAll();

              Then, provide a concrete GWT service offering CurrencyService functionalities in a Spring context. This will be achieved by extending ApplicationObjectSupport and implementing CurrencyService:
              PHP Code:
              package fr.free.hd.ch4mp.tuto.server;

              import java.util.List;

              import org.springframework.context.support.ApplicationObjectSupport;

              import fr.free.hd.ch4mp.support.gwt.GwtRpcEndPoint;
              import fr.free.hd.ch4mp.tuto.business.currency.Currency;
              import fr.free.hd.ch4mp.tuto.business.currency.CurrencyRepository;
              import fr.free.hd.ch4mp.tuto.business.currency.CurrencyService;

              @
              GwtRpcEndPoint
              public class CurrencyServiceImpl extends ApplicationObjectSupport implements
                      
              CurrencyService {
                  private 
              CurrencyRepository currencyRepository;
                  
                  public 
              CurrencyServiceImpl(CurrencyRepository currencyRepository) {
                      
              this.currencyRepository currencyRepository;
                  }

                  public 
              Currency[] getAllCurrencies() {
                      List<
              Currencycurrencies currencyRepository.retrieveAll();
                      return 
              currencies.toArray(new Currency[currencies.size()]);
                  }

              At last provide a stub implementation for currency repository:
              PHP Code:
              package fr.free.hd.ch4mp.tuto.business.currency;

              import java.util.ArrayList;
              import java.util.List;

              public class 
              StubCurrencyRepository implements CurrencyRepository {
                  @
              Override
                  
              public List<CurrencyretrieveAll() {
                      List<
              Currencyresult = new ArrayList<Currency>(3);
                      try {
                          
              result.add(new Currency("EUR"2RoundingMethod.CLOSER));
                          
              result.add(new Currency("JPY"0RoundingMethod.TRUNCATE));
                          
              result.add(new Currency("USD"2RoundingMethod.CLOSER));
                          return 
              result;
                      } catch (
              Exception e) {
                          return 
              null;
                      }
                  }

              Unit test CurrencyServiceImpl

              We won't test stubs. Actually stubs most frequent usage is to facilitate unit testing: their function is to get rid of potential persistence problems and complexity.
              So create two test cases: one for unit testing CurrencyServiceImpl (right click the class and proceed as you did for Currency) and an other for testing CurrencyService in Spring context, but we will see this after we have configured Spring webapp.
              PHP Code:
              package fr.free.hd.ch4mp.tuto.server;

              import static org.junit.Assert.assertNotNull;
              import static org.junit.Assert.assertTrue;
              import static org.junit.Assert.fail;

              import org.junit.Before;
              import org.junit.Test;

              import fr.free.hd.ch4mp.tuto.business.currency.Currency;
              import fr.free.hd.ch4mp.tuto.business.currency.StubCurrencyRepository;

              public class 
              CurrencyServiceImplTest {
                  private 
              CurrencyServiceImpl currencyService;
                  
                  @
              Before
                  
              public void setUp() throws Exception {
                      
              StubCurrencyRepository currencyRepo = new StubCurrencyRepository();
                      
              currencyService = new CurrencyServiceImpl(currencyRepo);
                  }
                  
                  @
              Test
                  
              public void testGetAllCurrencies() throws Exception{
                      
              Currency[] expected = new Currency[]{
                              new 
              Currency("EUR"2RoundingMethod.CLOSER),
                              new 
              Currency("JPY"0RoundingMethod.TRUNCATE),
                              new 
              Currency("USD"2RoundingMethod.CLOSER)};
                      
              Currency[] returned currencyService.getAllCurrencies();
                      
              assertNotNull(returned);
                      if(
              expected.length != returned.length)
                          
              fail("Service returned a wrong number of currencies");
                      for(
              int i=0i<expected.lengthi++){
                          
              assertTrue(expected[i].equals(returned[i]));
                      }
                  }

              Comment


              • #8
                tuto-server part2

                Web-app setup

                Edit /src/main/webapp/WEB-INF/web.xml. Change display-name to GWT / Spring / Hibernate /Maven2 / eclipse tutorial. Define one DispatcherServlet named currencyService and map all requests ending with .service to it.
                Code:
                <!DOCTYPE web-app PUBLIC
                 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
                 "http://java.sun.com/dtd/web-app_2_3.dtd" >
                
                <web-app>
                    <display-name>GWT / Spring / Hibernate / Maven2 / eclipse tutorial</display-name>
                    
                    <servlet>
                        <servlet-name>currencyService</servlet-name>
                        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
                        <load-on-startup>1</load-on-startup>
                    </servlet>
                    <servlet-mapping>
                        <servlet-name>currencyService</servlet-name>
                        <url-pattern>*.service</url-pattern>
                    </servlet-mapping>
                </web-app>
                Spring context setup
                We want Spring to auto detect GWT-RPC services (based on @GwtRpcEndPoint annotation). We also want container to use custom URL handler mapping and auto wrap services in handler adapter.
                Create a new currencyService-servlet.xml file in src/main/webapp/WEB-INF. This file contains simply references application and infrastructure configuration files. Here is the content:
                Code:
                <?xml version="1.0" encoding="UTF-8"?>
                <beans 
                    xmlns="http://www.springframework.org/schema/beans"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xsi:schemaLocation="
                        http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
                    
                    <import resource="classpath:fr/free/hd/ch4mp/tuto/server/stub-application-config.xml" />
                    <import resource="classpath:system-stub-config.xml" />
                    
                </beans>
                Now, create a file named system-stub-config.xml in src/main/resources:
                Code:
                <?xml version="1.0" encoding="UTF-8"?>
                <beans 
                    xmlns="http://www.springframework.org/schema/beans"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xsi:schemaLocation="
                        http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
                
                    <bean class="fr.free.hd.ch4mp.support.gwt.GwtHandlerAdapter"/>
                
                    <bean class="fr.free.hd.ch4mp.support.gwt.GwtUrlHandlerMapping">
                        <property name="prefix" value="/"/>
                        <property name="suffix" value=".service"/>
                    </bean>
                </beans>
                Finaly, create a new fr.free.hd.ch4mp.tuto.server package in src/main/resources and add an new stub-application-config.xml file in it:
                Code:
                <?xml version="1.0" encoding="UTF-8"?>
                <beans 
                    xmlns="http://www.springframework.org/schema/beans"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xsi:schemaLocation="
                        http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
                    
                    <bean id="currencyService" class="fr.free.hd.ch4mp.tuto.server.CurrencyServiceImpl" >
                        <constructor-arg ref="currencyRepository" />
                    </bean>
                    
                    <bean id="currencyRepository" class="fr.free.hd.ch4mp.tuto.business.currency.StubCurrencyRepository" />
                </beans>
                Integration testing
                In src/test/java directory, right click fr.free.hd.ch4mp.tuto.server package to add a new JUnit test case named CurrencyServiceTest:
                PHP Code:
                package fr.free.hd.ch4mp.tuto.server;

                import org.junit.Test;
                import org.springframework.test.AbstractDependencyInjectionSpringContextTests;

                import fr.free.hd.ch4mp.tuto.business.currency.Currency;
                import fr.free.hd.ch4mp.tuto.business.currency.CurrencyService;

                public class 
                CurrencyServiceTest extends AbstractDependencyInjectionSpringContextTests {
                    private 
                CurrencyService currencyService;
                    
                    public 
                void setCurrencyService(CurrencyService currencyService) {
                        
                this.currencyService currencyService;
                    }

                    @
                Override
                    
                protected String[] getConfigLocations() {
                        return new 
                String[]{"classpath:fr/free/hd/ch4mp/tuto/server/stub-application-config.xml"};
                    }
                    
                    @
                Test
                    
                public void testGetAllCurrencies() throws Exception{
                        
                Currency[] expected = new Currency[]{new Currency("EUR"), new Currency("GBP"), new Currency("USD")};
                        
                Currency[] returned currencyService.getAllCurrencies();
                        
                assertNotNull(returned);
                        if(
                expected.length != returned.length)
                            
                fail("Service returned a wrong number of currencies");
                        for(
                int i=0i<expected.lengthi++){
                            
                assertTrue(expected[i].equals(returned[i]));
                        }
                    }


                Build and deploy to Jetty
                Open menu Run->Run Configurations... right click Maven Build and select New. Set server_jetty_run-war as name, ${workspace_loc:/tuto-server} as base directory and org.mortbay.jetty:maven-jetty-plugin:6.1.14:run-war as goal (you may adapt version number, here 6.1.14, to the version number you got when choosing jetty plugin in pom.xml configuration). Do nut click Run for now, we need stuff from client project first.
                Also add a run target called server_jetty_stop with the same base directory and org.mortbay.jetty:maven-jetty-plugin:6.1.14:stop target.

                Comment


                • #9
                  tuto-client

                  Now that we have a service running, we want a client to use it. Using a maven archetype, we will have a configured project within a few clicks, and after minimal Java coding, we'll have a web client for our service.
                  Project setup

                  Create a new Maven project and uncheck Resolve Workspace projects once again in Advanced tab. Click Next and filter archetypes with maven and choose maven-archetype-gwt (net.sf.mgp). Set tuto-client as artifact id, 1.0 as version and fr.free.hd.ch4mp.tuto for both group id and package (remove trailing tuto-client at the end of package). Click Finish.
                  Edit project properties to disable java compiler specific settings and set project facet->Java properties to 1.6
                  Edit client/pom.xml. In overview tab, add one property: platform set to windows. Then open dependencies tab to:
                  • remove junit
                  • remove gwt-servlet
                  • add gwt-dev with ${gwt.version} as version and ${platform} as classifier
                  • add tuto-dto (fr.free.hd.ch4mp.tuto)
                  • add tuto-dto (fr.free.hd.ch4mp.tuto) a second time and set sources as classifier
                  • remove maven-gwt-plugin
                  • add gwt-maven-plugin (org.codehaus.mojo). Add an execution with hostedModeBrowser id and an eclipse goal. The same way, add an other exectution with compile as id and goal. Click configuration to add a module configuration:
                  Code:
                          <plugin>
                                  <groupId>org.codehaus.mojo</groupId>
                                  <artifactId>gwt-maven-plugin</artifactId>
                                  <version>1.0</version>
                                  <executions>
                                      <execution>
                                          <id>hostedModeBrowser</id>
                                          <configuration>
                                              <module>fr.free.hd.ch4mp.tuto.CurrencyViewer</module>
                                          </configuration>
                                          <goals>
                                              <goal>eclipse</goal>
                                          </goals>
                                      </execution>
                                      <execution>
                                          <id>compile</id>
                                          <configuration>
                                              <module>fr.free.hd.ch4mp.tuto.CurrencyViewer</module>
                                          </configuration>
                                          <goals>
                                              <goal>compile</goal>
                                          </goals>
                                      </execution>
                                  </executions>
                              </plugin>
                  Rename src/main/java/fr/free/hd/ch4mp/tuto/App.gwt.xml to CurrencyViewer.gwt.xml and edit it. Replace entry-point class App with CurrencyViewer. We also need to inherit the Business gwt module we have defined in tuto-dto project.
                  Code:
                  <?xml version="1.0" encoding="UTF-8"?>
                  <module>
                      <!-- Inherit the core Web Toolkit stuff.                  -->
                      <inherits name="com.google.gwt.user.User"/>
                      
                      <inherits name="fr.free.hd.ch4mp.tuto.Business"/>
                      
                      <!-- Specify the app entry point class.                   -->
                      <entry-point class="fr.free.hd.ch4mp.tuto.client.CurrencyViewer"/>
                  </module>
                  Rename src/main/java/fr/free/hd/ch4mp/tuto/public/index.html to CurrencyViewer.html, edit it and replace the two App occurencies with CurrencyViewer.
                  Rename fr.free.hd.ch4mp.tuto.client.App class to CurrencyViewer.

                  Java code
                  We will build a very basic screen with only two elements (widgets in GWT world):
                  • a flex table to display data returned by the currency service
                  • a load button to lunch service call (and also have an example of GWT event handling)
                  The onLoadModule method is required by GWT to initiate screen
                  PHP Code:
                  package fr.free.hd.ch4mp.tuto.client;

                  import com.google.gwt.core.client.EntryPoint;
                  import com.google.gwt.core.client.GWT;
                  import com.google.gwt.user.client.rpc.AsyncCallback;
                  import com.google.gwt.user.client.rpc.ServiceDefTarget;
                  import com.google.gwt.user.client.ui.Button;
                  import com.google.gwt.user.client.ui.ClickListener;
                  import com.google.gwt.user.client.ui.FlexTable;
                  import com.google.gwt.user.client.ui.RootPanel;
                  import com.google.gwt.user.client.ui.VerticalPanel;
                  import com.google.gwt.user.client.ui.Widget;

                  import fr.free.hd.ch4mp.tuto.business.currency.Currency;
                  import fr.free.hd.ch4mp.tuto.business.currency.CurrencyService;
                  import fr.free.hd.ch4mp.tuto.business.currency.CurrencyServiceAsync;

                  public class 
                  CurrencyViewer implements EntryPoint {
                      
                  //GWT widgets
                      
                  private VerticalPanel mainPanel = new VerticalPanel();
                      private 
                  FlexTable currenciesDisplayTable = new FlexTable();
                      private 
                  Button loadButton = new Button("Load");
                      
                      
                  /**
                       * Required by GWT, used to initiate the screen
                       * @see com.google.gwt.core.client.EntryPoint#onModuleLoad()
                       */
                      
                  public void onModuleLoad() {
                          
                  //create an event handler and add it to the button
                          
                  loadButton.addClickListener(new ClickListener(){
                              public 
                  void onClick(Widget sender) {
                                  
                  retrieveAllCurrencies();
                              }
                          });
                          
                  //we want the FlexTable to come first and the Button second
                          
                  mainPanel.add(currenciesDisplayTable);
                          
                  mainPanel.add(loadButton);
                          
                  RootPanel.get().add(mainPanel);
                      }
                      
                      
                  /**
                       * Lunches an asynchronous call GWT currency service
                       */
                      
                  private void retrieveAllCurrencies(){
                          
                  //Asynchronous calls require callbacks, so let's create one
                          
                  AsyncCallback<Currency[]> callback = new AsyncCallback<Currency[]>(){
                              public 
                  void onFailure(Throwable caught) {
                                  
                  refreshCurrenciesDisplayTable(null);
                              }

                              public 
                  void onSuccess(Currency[] result) {
                                  
                  refreshCurrenciesDisplayTable(result);
                              }
                          };
                          
                          
                  //Ask GWT to create for us an asynchronous currency service corresponding to the synchronous we have implemented
                          
                  CurrencyServiceAsync currencyService GWT.create(CurrencyService.class);
                          
                  //define how to contact the service
                          
                  ServiceDefTarget endpoint = (ServiceDefTargetcurrencyService;
                          
                  endpoint.setServiceEntryPoint("/tuto-server/fr/free/hd/ch4mp/tuto/business/currency/CurrencyService.service");
                          
                  //call the service
                          
                  currencyService.getAllCurrencies(callback);
                      }
                      
                      
                  /**
                       * @param currencies
                       * Flushes display table and then display data for all provided currencies
                       */
                      
                  private void refreshCurrenciesDisplayTable(Currency[] currencies){
                          
                  currenciesDisplayTable.clear();
                          
                  currenciesDisplayTable.setText(00"ISO code");
                          
                  currenciesDisplayTable.setText(01"Decimals number");
                          
                  currenciesDisplayTable.setText(02"Rounding Method");
                          if(
                  currencies != null){
                              
                  int i=1;
                              for(
                  Currency curr:currencies){
                                  
                  currenciesDisplayTable.setText(i0curr.getIsoCode());
                                  
                  currenciesDisplayTable.setText(i1curr.getDecimals().toString());
                                  
                  currenciesDisplayTable.setText(i2curr.getRoundingMethod().toString());
                                  
                  i++;
                              }
                          }
                      }
                      

                  HTML code
                  Edit CurrencyViewer.html.
                  Code:
                  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
                  
                  <html>
                  <head>
                      <title>Currency viewer</title>
                      <meta name="gwt:module" content="fr.free.hd.ch4mp.tuto.CurrencyViewer" />
                  </head>
                  <body>
                      <h1>Currency viewer</h1>
                      <script language="javascript" src="fr.free.hd.ch4mp.tuto.CurrencyViewer.nocache.js" type="text/javascript"></script>
                      <!-- OPTIONAL: include this if you want history support -->
                      <iframe id="__gwt_historyFrame" style="width: 0; height: 0; border: 0"></iframe>
                  </body>
                  </html>
                  Build and deploy
                  Create a new Maven run configuration with ${workspace_loc:/tuto-client}\ as Base Directory and org.codehaus.mojo:gwt-maven-plugin:1.0:eclipse as Goals. Run it.
                  Create a new Maven run configuration with ${workspace_loc:/tuto-client}\ as Base Directory and org.codehaus.mojo:gwt-maven-plugin:1.0:compile as Goals. Then go to JRE tab and add those arguments: -Xmx256m. Click Run button.
                  Select Client project in project explorer and press F5. Edit fr.free.hd.ch4mp.tuto.CurrencyViewer.launch to modify PROGRAM_ARGUMENTS (run in no server mode). Here is the code:
                  <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-noserver -out target\client-1.0 http://localhost:9090/tuto-server/CurrencyViewer.html"/>
                  Copy every file that does NOT end with .cache.html (*.gif, *.gwt.rpc, *.nocache.js and *.html) from client/target/client-1.0/fr.free.hd.ch4mp.tuto.CurrencyViewer to server/src/main/webapp.
                  Start Jetty (run server_jetty_run-war).
                  To finish, select client project in project explorer and click run button (green one with white arrow).

                  Comment


                  • #10
                    Connect server project to a relational database part1

                    Right now, the service we expose is not of great interest. It would be much better if the currency list was kept in a database instead of being hard-coded.

                    Project setup modification

                    We need to add a few dependencies in pom.xml but, for licensing reasons, one isn't in the default maven repo. So open tuto-server/pom.xml and brose to repositories tab. Then add a new one with jta as Id and http://download.java.net/maven/2/javax/transaction/jta/1.0.1B/ as URL.
                    Then save and move to dependencies tab to add:
                    • spring (org.springframework). You can limit scope to Runtime
                    • spring-jdbc (org.springframework)
                    • spring-hibernate3 (org.springframework)
                    • hibernate (org.hibernate)
                    • jta (javax.transaction). You can limit scope to Runtime
                    • mysql-connector-java (mysql)
                    Database
                    We only need one currency table with two fields: numeric Id and ISO code. Actually, ISO code alone could be enough as unicity is guaranteed and every currency has an ISO code, but I like to have auto incremented numeric Ids for every entities. We will write three scripts, one to create database structure, and one to populate the database structure.
                    Create a sql source directory in src/main. Then create two sql files in src/main/sql:[list][*]mysqlDbCreation.sql
                    Code:
                    DROP TABLE IF EXISTS `tuto`.`Currency`;
                    
                    CREATE TABLE IF NOT EXISTS `tuto`.`Currency`(
                        `id`        INT            AUTO_INCREMENT,
                        `isoCode`    CHAR(3)        UNIQUE NOT NULL,
                        `decimals`    INT            NOT NULL,
                        `rounding`    VARCHAR(10)    NOT NULL,
                        PRIMARY KEY(`id`)
                    );
                    
                    ALTER TABLE `tuto`.`Currency`
                    ADD CONSTRAINT CK_CURRENCY_ROUNDING
                    CHECK `rounding` IN ('CLOSER', 'TRUNCATE');
                    [*]mysqlDbPopulate.sql
                    Code:
                    INSERT INTO `tuto`.`Currency` (`isoCode`, `decimals`, `rounding`) VALUES 
                        ('AUD', 2, 'CLOSER'),
                        ('JPY', 0, 'TRUNCATE'),
                        ('EUR', 2, 'CLOSER'),
                        ('USD', 2, 'CLOSER'),
                        ('NZD', 2, 'CLOSER'),
                        ('GBP', 2, 'CLOSER');
                    In the header of each SQL source file, select the database profile we have created at the beginning (MySQL_5.1 / tuto_localhost / tuto). Then right click db creation file an select ExecuteSQL Files.
                    Do the same with db populate. Congratulation, you have a test database ready for use.
                    Java code

                    Let's implement an other currency repository in the same package as StubCurrencyRepository, HibernateCurrencyRepository:
                    PHP Code:
                    package fr.free.hd.ch4mp.tuto.business.currency;


                    import java.util.List;


                    import org.hibernate.Query;

                    import org.hibernate.Session;

                    import org.hibernate.SessionFactory;

                    import org.springframework.transaction.annotation.Transactional;


                    public class 
                    HibernateCurrencyRepository implements CurrencyRepository {

                    private 
                    SessionFactory sessionFactory;


                    public 
                    HibernateCurrencyRepository(SessionFactory sessionFactory) {

                    this.sessionFactory sessionFactory;

                    }


                    @
                    Override

                    @Transactional

                    public List<CurrencyretrieveAll() {

                    Query query getCurrentSession().createQuery("from Currency");

                    return 
                    query.list();

                    }

                    protected 
                    Session getCurrentSession() {

                    return 
                    sessionFactory.getCurrentSession();

                    }


                    We need a MySQL data source factory that can provide us with database connections:
                    PHP Code:
                    package fr.free.hd.ch4mp.tuto;

                    import javax.sql.DataSource;

                    import org.springframework.beans.factory.FactoryBean;
                    import org.springframework.beans.factory.InitializingBean;
                    import org.springframework.jdbc.datasource.DriverManagerDataSource;

                    public class 
                    MysqlDataSourceFactory implements FactoryBeanInitializingBean {
                        private 
                    String databaseName;
                        private 
                    String userName;
                        private 
                    String password;
                        private 
                    DataSource dataSource;

                        public 
                    MysqlDataSourceFactory(){
                        }
                        
                        public 
                    MysqlDataSourceFactory(String databaseNameString userNameString password) {
                            
                    setDatabaseName(databaseName);
                            
                    setUserName(userName);
                            
                    setPassword(password);
                        }

                        public 
                    void setDatabaseName(String databaseName) {
                            
                    this.databaseName databaseName;
                        }

                        public 
                    void setUserName(String userName) {
                            
                    this.userName userName;
                        }

                        public 
                    void setPassword(String password) {
                            
                    this.password password;
                        }

                        public 
                    DataSource getDataSource() {
                            if (
                    dataSource == null) {
                                
                    dataSource createDataSource();
                            }
                            return 
                    dataSource;
                        }

                        @
                    Override
                        
                    public void afterPropertiesSet() throws Exception {
                            if (
                    databaseName == null) {
                                throw new 
                    IllegalArgumentException("The name of the test database to create is required");
                            }
                            
                    this.dataSource createDataSource();
                        }

                        @
                    Override
                        
                    public Object getObject() throws Exception {
                            return 
                    getDataSource();
                        }

                        @
                    Override
                        
                    public Class<DataSourcegetObjectType() {
                            return 
                    DataSource.class;
                        }

                        @
                    Override
                        
                    public boolean isSingleton() {
                            return 
                    true;
                        }
                        
                        public static 
                    DataSource createDataSource(String databaseNameString userNameString password) {
                            return new 
                    MysqlDataSourceFactory(databaseNameuserNamepassword).getDataSource();
                        }

                        private 
                    DataSource createDataSource() {
                            
                    DriverManagerDataSource dataSource = new DriverManagerDataSource();
                            
                    // use the HsqlDB JDBC driver
                            
                    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
                            
                    // have it create an in-memory database
                            
                    dataSource.setUrl("jdbc:mysql://localhost:3306/" databaseName);
                            
                    dataSource.setUsername(userName);
                            
                    dataSource.setPassword(password);
                            return 
                    dataSource;
                        }

                    To finish, we need to give hibernate a way to create a RoundingMethod from a database string and the oposite (database string from a RoundingMethod). This is done by implementing UserType:
                    PHP Code:
                    package fr.free.hd.ch4mp.tuto.business.currency;

                    import java.io.Serializable;
                    import java.sql.PreparedStatement;
                    import java.sql.ResultSet;
                    import java.sql.SQLException;
                    import java.sql.Types;

                    import org.hibernate.HibernateException;
                    import org.hibernate.usertype.UserType;

                    public class 
                    RoundingMethodUserType implements UserType {
                        private static final 
                    int[] SQL_TYPES = {Types.VARCHAR};

                        @
                    Override
                        
                    public Object assemble(Serializable cachedObject owner)
                                
                    throws HibernateException {
                            return 
                    cached;
                        }

                        @
                    Override
                        
                    public Object deepCopy(Object arg0throws HibernateException {
                            return 
                    arg0;
                        }

                        @
                    Override
                        
                    public Serializable disassemble(Object arg0throws HibernateException {
                            return (
                    Serializablearg0;
                        }

                        @
                    Override
                        
                    public boolean equals(Object arg0Object arg1throws HibernateException {
                            return 
                    arg0 == arg1;
                        }

                        @
                    Override
                        
                    public int hashCode(Object arg0throws HibernateException {
                            return 
                    arg0.hashCode();
                        }

                        @
                    Override
                        
                    public boolean isMutable() {
                            return 
                    false;
                        }

                        @
                    Override
                        
                    public Object nullSafeGet(ResultSet resultSetString[] namesObject owner)
                                
                    throws HibernateExceptionSQLException {
                            
                    String name resultSet.getString(names[0]);
                            if(
                    resultSet.wasNull())
                                return 
                    null;
                            if(
                    name.equals("CLOSER"))
                                return 
                    RoundingMethod.CLOSER;
                            if(
                    name.equals("TRUNCATE"))
                                return 
                    RoundingMethod.TRUNCATE;
                            throw new 
                    SQLException("Unknown rounding method in database");
                        }

                        @
                    Override
                        
                    public void nullSafeSet(PreparedStatement statementObject valueint index)
                                
                    throws HibernateExceptionSQLException {
                            if (
                    value == null) {
                                
                    statement.setNull(indexTypes.VARCHAR);
                            }else{
                                
                    statement.setString(indexvalue.toString());
                            }
                        }

                        @
                    Override
                        
                    public Object replace(Object originalObject targetObject owner)
                                
                    throws HibernateException {
                            return 
                    original;
                        }

                        @
                    Override
                        
                    public Class<RoundingMethodreturnedClass() {
                            return 
                    RoundingMethod.class;
                        }

                        @
                    Override
                        
                    public int[] sqlTypes() {
                            return 
                    SQL_TYPES;
                        }

                    Comment


                    • #11
                      Connect server project to a relational database part2

                      hibernate configuration
                      Add a new Currency.hbm.xml in resources/fr/free/hd/ch4mp/tuto/business/currency:
                      Code:
                      <?xml version="1.0" encoding="UTF-8"?>
                      <!DOCTYPE hibernate-mapping PUBLIC 
                          "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
                          "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
                      
                      <hibernate-mapping package="fr.free.hd.ch4mp.tuto.business.currency" default-access="field" default-lazy="false">
                          <class name="Currency" table="Currency">
                              <id name="id" column="id"/>
                              <property name="isoCode" column="isoCode"/>
                              <property name="decimals" column="decimals"/>
                      		<property name="roundingMethod" column="rounding" type="fr.free.hd.ch4mp.tuto.business.currency.RoundingMethodUserType"/>
                          </class>
                      </hibernate-mapping>
                      Spring configuration
                      Now we need to provide a new application configuration with hibernate repository instead of stub. In resources/fr/free/hd/ch4mp/tuto/server, create a new hibernate-application-config.xml file:
                      Code:
                      <?xml version="1.0" encoding="UTF-8"?>
                      <beans 
                      	xmlns="http://www.springframework.org/schema/beans"
                      	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                      	xmlns:tx="http://www.springframework.org/schema/tx"
                      	xsi:schemaLocation="http://www.springframework.org/schema/beans
                      						http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                      						http://www.springframework.org/schema/tx
                      						http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
                      	
                      	<bean id="currencyService" class="fr.free.hd.ch4mp.tuto.server.CurrencyServiceImpl" >
                      		<constructor-arg ref="currencyRepository" />
                      	</bean>
                      	
                      	<bean id="currencyRepository" class="fr.free.hd.ch4mp.tuto.business.currency.HibernateCurrencyRepository" >
                      		<constructor-arg ref="sessionFactory" />
                      	</bean>
                      	
                      	<tx:annotation-driven transaction-manager="transactionManager" />
                      </beans>
                      We also need to provide a new system configuration that include a datasource, a session factory(with hibernate configuration) and transaction manager. In src/main/webapp/WEB-INF, add system-hibernate-config.xml:
                      Code:
                      <?xml version="1.0" encoding="UTF-8"?>
                      <beans 
                      	xmlns="http://www.springframework.org/schema/beans"
                      	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                      	xsi:schemaLocation="
                      		http://www.springframework.org/schema/beans
                      		http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
                      
                      	<bean class="fr.free.hd.ch4mp.support.gwt.GwtHandlerAdapter"/>
                      
                      	<bean class="fr.free.hd.ch4mp.support.gwt.GwtUrlHandlerMapping">
                      		<property name="prefix" value="/"/>
                      		<property name="suffix" value=".service"/>
                      	</bean>
                      	
                      	<bean id="dataSource" class="fr.free.hd.ch4mp.tuto.MysqlDataSourceFactory">
                      		<property name="databaseName" value="tuto"/>
                      		<property name="userName" value="tuto"/>
                      		<property name="password" value="toto"/>
                      	</bean>
                      
                      	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean" >
                      		<property name="dataSource" ref="dataSource" />
                      		<property name="mappingLocations" >
                      			<list>
                      				<value>classpath:fr/free/hd/ch4mp/tuto/business/currency/Currency.hbm.xml</value>
                      			</list>
                      		</property>
                      		<property name="hibernateProperties">
                      			<value>
                      				hibernate.show_sql=true
                      				hibernate.format_sql=true
                      				hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
                      			</value>
                      		</property>
                      	</bean>
                      
                      	<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
                      		<property name="sessionFactory" ref="sessionFactory" />
                      	</bean>
                      </beans>
                      Finally, edit currencyServlet.xml to replace the inclusion of stub config file with hibernate ones:
                      Code:
                      <?xml version="1.0" encoding="UTF-8"?>
                      <beans 
                      	xmlns="http://www.springframework.org/schema/beans"
                      	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                      	xsi:schemaLocation="
                      		http://www.springframework.org/schema/beans
                      		http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
                      	
                      	<import resource="classpath:fr/free/hd/ch4mp/tuto/server/hibernate-application-config.xml" />
                      	<import resource="classpath:system-hibernate-config.xml" />
                      	
                      </beans>
                      Tests modification
                      It is possible to code an other data source factory that would automatically drop database, recreate it and repopulate it. That would allow us to have the same representative data set in database at the beginning of each test.
                      A nice test performance improvement would be to use HSQLDB instead of MySQL. In memory database is much faster when it comes to drop and recreate a small database many times.
                      All this requires codding new SQL scripts, new data source factory and new test. It's a bit long and out of scope here, but I advise you do all that when you go back to real life.

                      Comment


                      • #12
                        Modify client project to use gwt-ext widgets

                        GWT native widget library is quite limited, but many other exist. gwt-ext is an example of very rich library well documented and freely available, so let's go for this one.

                        Modify project configuration
                        The library has to be taken from a special maven repository. Open pom.xml, move to repositories tab and add a new repository with gwtext as id and http://www.gwt-ext.com/maven2/ as URL.
                        Then move to dependencies tab and Add... button. Input com.gwtext as group id, gwetext as artifact id and 2.0.5 (or whatever is available in http://www.gwt-ext.com/maven2/com/gwtext/gwtext/) as version.
                        You also need to download ExtJS (http://yogurtearl.com/ext-2.0.2.zip) and unpack it somewhere you like (C:\dev\ext-2.0.2 for instance).
                        In tuto-client/src/webapp and tuto-server/src/webapp, create js/ext/adapter directory. Copy C:\dev\ext-2.0.2\adapter\ext to src/webapp/js/adapter/ext. Also copy ext-all.js and ext-core.js from C:\dev\ext-2.0.2 to src/webapp/js/.

                        Java code
                        All we have to do is to replace GWT widgets with GWTEXT widgets. We also remove the useless button to auto load data:
                        PHP Code:
                        package fr.free.hd.ch4mp.tuto.client;

                        import com.google.gwt.core.client.EntryPoint;
                        import com.google.gwt.core.client.GWT;
                        import com.google.gwt.user.client.rpc.AsyncCallback;
                        import com.google.gwt.user.client.rpc.ServiceDefTarget;
                        import com.google.gwt.user.client.ui.RootPanel;
                        import com.gwtext.client.data.ArrayReader;
                        import com.gwtext.client.data.FieldDef;
                        import com.gwtext.client.data.MemoryProxy;
                        import com.gwtext.client.data.RecordDef;
                        import com.gwtext.client.data.Store;
                        import com.gwtext.client.data.StringFieldDef;
                        import com.gwtext.client.data.IntegerFieldDef;
                        import com.gwtext.client.widgets.Panel;
                        import com.gwtext.client.widgets.grid.ColumnConfig;
                        import com.gwtext.client.widgets.grid.ColumnModel;
                        import com.gwtext.client.widgets.grid.GridPanel;

                        import fr.free.hd.ch4mp.tuto.business.currency.Currency;
                        import fr.free.hd.ch4mp.tuto.business.currency.CurrencyService;
                        import fr.free.hd.ch4mp.tuto.business.currency.CurrencyServiceAsync;

                        public class 
                        CurrencyViewer implements EntryPoint {
                            
                        //GWT widgets
                            
                        private Panel mainPanel;
                            private 
                        RecordDef recordDef;
                            private 
                        ColumnModel columnModel;
                            private 
                        ArrayReader reader;
                            private 
                        GridPanel currenciesDisplayTable;
                            
                            
                        /**
                             * Required by GWT, used to initiate the screen
                             * @see com.google.gwt.core.client.EntryPoint#onModuleLoad()
                             */
                            
                        public void onModuleLoad() {
                                
                        mainPanel = new Panel();
                                
                        mainPanel.setTitle("Currencies");
                                
                        mainPanel.setCollapsible(true);
                                
                        mainPanel.setAnimCollapse(true);
                                
                                
                        currenciesDisplayTable = new GridPanel();
                                
                        recordDef = new RecordDef(
                                        new 
                        FieldDef[]{
                                                new 
                        StringFieldDef("ISO code"),
                                                new 
                        IntegerFieldDef("decimals"),
                                                new 
                        StringFieldDef("rounding method")
                                        });
                                
                        reader = new ArrayReader(recordDef);
                                
                        ColumnConfig[] columns = new ColumnConfig[]{
                                        new 
                        ColumnConfig("ISO code""ISO code"75true),
                                        new 
                        ColumnConfig("decimals""decimals"75true),
                                        new 
                        ColumnConfig("rounding method""rounding method"150true)
                                };
                                
                        columnModel = new ColumnModel(columns);
                                
                        MemoryProxy proxy = new MemoryProxy(new Object[0][0]);
                                
                        Store store = new Store(proxyreader);
                                
                        store.load();
                                
                        currenciesDisplayTable.setStore(store);
                                
                        currenciesDisplayTable.setColumnModel(columnModel);
                                
                        currenciesDisplayTable.setStripeRows(true);
                                
                        currenciesDisplayTable.setAutoHeight(true);
                                
                        currenciesDisplayTable.setAutoWidth(true);
                                
                        mainPanel.add(currenciesDisplayTable);

                                
                        mainPanel.setAutoHeight(true);
                                
                        mainPanel.setAutoWidth(true);
                                
                        RootPanel.get().add(mainPanel);
                                
                                
                        retrieveAllCurrencies();
                            }
                            
                            
                        /**
                             * Lunches an asynchronous call GWT currency service
                             */
                            
                        private void retrieveAllCurrencies(){
                                
                        //Asynchronous calls require callbacks, so let's create one
                                
                        AsyncCallback<Currency[]> callback = new AsyncCallback<Currency[]>(){
                                    public 
                        void onFailure(Throwable caught) {
                                        
                        refreshCurrenciesDisplayTable(null);
                                    }

                                    public 
                        void onSuccess(Currency[] result) {
                                        
                        refreshCurrenciesDisplayTable(result);
                                    }
                                };
                                
                                
                        //Ask GWT to create for us an asynchronous currency service corresponding to the synchronous we have implemented
                                
                        CurrencyServiceAsync currencyService GWT.create(CurrencyService.class);
                                
                        //define how to contact the service
                                
                        ServiceDefTarget endpoint = (ServiceDefTargetcurrencyService;
                                
                                
                        //TODO find a way to configure this (remove dirty hard coded servlet name and URL extension)
                                //TODO create a super class for GWT RPC consumers that can auto map service URL
                                
                        String endPointName CurrencyService.class.getName().replace('.''/');
                                
                        endpoint.setServiceEntryPoint("/tuto-server/" endPointName ".service");
                                
                                
                        //call the service
                                
                        currencyService.getAllCurrencies(callback);
                            }
                            
                            
                        /**
                             * @param currencies
                             * Flushes display table and then display data for all provided currencies
                             */
                            
                        private void refreshCurrenciesDisplayTable(Currency[] currencies){
                                
                        Object[][] data = new Object[currencies.length][3];
                                
                        int i=0;
                                for(
                        Currency curr:currencies){
                                    
                        data[i][0] = curr.getIsoCode();
                                    
                        data[i][1] = new Integer(curr.getDecimals());
                                    
                        data[i][2] = curr.getRoundingMethod().toString();
                                    
                        i++;
                                }
                                
                        MemoryProxy proxy = new MemoryProxy(data);
                                
                        Store store = new Store(proxyreader);
                                
                        store.load();
                                
                        currenciesDisplayTable.getStore().removeAll();
                                
                        currenciesDisplayTable.getStore().add(store.getRecords());
                            }
                            

                        Comment


                        • #13
                          Hi,
                          Thanks for the tutorial. Is it possible to provide a downloadable version? It did not work for me, and it's possible I misinterpreted some of the steps. A downloadable version would eliminate this.
                          Thanks,
                          -jim

                          Comment

                          Working...
                          X