miércoles, 25 de marzo de 2015

Proyecto

El objetivo de este post es crear un proyecto sencillo utilizando los framerworks Java: Sencha GXT, Spring e Hibernate de manera intregrada usando el modelo MVC de la misma forma en que lo hicimos en el blog http://vaadinspringhibernate.blogspot.com/

El código del proyecto lo pueden encontrar en github construido con maven

En este caso también asumimos que ustedes conocen el framework Spring, Hibernate y GWT, además de que Sencha es un framework tipo RIA que esta basado en GWT.

La configuración inicial es similar a la de este post salvo que como esta vez estamos trabajando con maven el archivo pom.xml es como sigue:

 <?xml version="1.0" encoding="UTF-8"?>  
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">  
   <modelVersion>4.0.0</modelVersion>  
   <groupId>com.ejemplo.sencha</groupId>  
   <artifactId>com.ejemplo.sencha</artifactId>  
   <packaging>war</packaging>  
   <version>1.0-SNAPSHOT</version>  
   <name>gxt-basic-public-3x</name>  
   <description>GXT maven setup</description>  
   <properties>  
     <!-- GXT -->  
     <gxt.version>3.1.1</gxt.version>  
     <!-- GWT -->  
     <gwt.version>2.6.1</gwt.version>  
     <gwt.style>OBF</gwt.style>  
     <spring.version>3.2.13.RELEASE</spring.version><!-- 3.0.5.RELEASE -->  
     <version.hibernate>3.5.6-Final</version.hibernate><!--3.5.3-Final-->  
     <version.slf4j>1.5.8</version.slf4j>  
     <version.log4j>1.2.14</version.log4j>  
     <!-- tell the compiler we can use 1.6 -->  
     <maven.compiler.source>1.6</maven.compiler.source>  
     <maven.compiler.target>1.6</maven.compiler.target>  
     <!-- Maven -->  
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
     <webappDirectory>${project.build.directory}/${project.build.finalName}</webappDirectory>  
   </properties>  
   <build>  
     <outputDirectory>${webappDirectory}/WEB-INF/classes</outputDirectory>  
     <resources>  
       <resource>  
         <directory>src/main/resources</directory>  
       </resource>  
     </resources>  
     <plugins>  
       <plugin>  
         <groupId>org.apache.maven.plugins</groupId>  
         <artifactId>maven-compiler-plugin</artifactId>  
         <version>3.0</version>  
         <configuration>  
           <source>1.7</source>  
           <target>1.7</target>  
         </configuration>  
       </plugin>  
       <!-- Skips the GWTTestCases during Junit Testing -->  
       <plugin>  
         <groupId>org.apache.maven.plugins</groupId>  
         <artifactId>maven-surefire-plugin</artifactId>  
         <version>2.12</version>  
         <configuration>  
           <includes>  
             <include>**/*Test.java</include>  
           </includes>  
           <excludes>  
             <exclude>**/*GwtTest.java</exclude>  
           </excludes>  
         </configuration>  
       </plugin>  
       <!-- GWT -->  
       <!-- 'mvn gwt:run' - runs development mode -->  
       <!-- 'mvn gwt:debug' - runs debug mode -->  
       <!-- 'mvn gwt:compile' - compiles gwt -->  
       <!-- 'mvn gwt:test' or 'mvn integration-test' - runs the gwt tests (*GwtTest.java), (run a suite its faster) -->  
       <plugin>  
         <groupId>org.codehaus.mojo</groupId>  
         <artifactId>gwt-maven-plugin</artifactId>  
         <version>${gwt.version}</version>  
         <configuration>  
           <strict>true</strict>  
           <testTimeOut>180</testTimeOut>  
           <includes>**/*GwtTestSuite.java</includes>  
           <excludes>**/*GwtTest.java</excludes>  
           <mode>htmlunit</mode>  
           <extraJvmArgs>-Xss1024K -Xmx1024M -XX:MaxPermSize=256M</extraJvmArgs>  
           <logLevel>INFO</logLevel>  
           <style>${gwt.style}</style>  
           <copyWebapp>true</copyWebapp>  
           <hostedWebapp>${webappDirectory}</hostedWebapp>  
           <port>8889</port>  
           <bindAddress>0.0.0.0</bindAddress>  
           <runTarget>Project.html</runTarget>  
           <module>com.ejemplo.sencha.Project</module>  
         </configuration>  
         <executions>  
           <execution>  
             <goals>  
               <goal>compile</goal>  
               <goal>test</goal>  
             </goals>  
           </execution>  
         </executions>  
       </plugin>  
       <!-- Copy static web files before executing gwt:run -->  
       <plugin>  
         <groupId>org.apache.maven.plugins</groupId>  
         <artifactId>maven-war-plugin</artifactId>  
         <version>2.1.1</version>  
         <executions>  
           <execution>  
             <phase>compile</phase>  
             <goals>  
               <goal>exploded</goal>  
             </goals>  
           </execution>  
         </executions>  
         <configuration>  
           <webappDirectory>${webappDirectory}</webappDirectory>  
         </configuration>  
       </plugin>  
       <plugin>  
         <groupId>org.apache.maven.plugins</groupId>  
         <artifactId>maven-compiler-plugin</artifactId>  
         <version>2.3.2</version>  
         <configuration>  
           <source>${maven.compiler.source}</source>  
           <target>${maven.compiler.target}</target>  
         </configuration>  
       </plugin>  
     </plugins>  
   </build>  
   <dependencies>  
     <!-- GXT -->  
     <!-- http://docs.sencha.com/gxt/3.1/getting_started/maven/Maven.html -->  
     <dependency>  
       <groupId>com.sencha.gxt</groupId>  
       <artifactId>gxt</artifactId>  
       <version>${gxt.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>com.sencha.gxt</groupId>  
       <artifactId>gxt-chart</artifactId>  
       <version>${gxt.version}</version>  
     </dependency>  
     <!-- GXT 3.1.0+ - Neptune Theme -->  
     <dependency>  
       <groupId>com.sencha.gxt</groupId>  
       <artifactId>gxt-theme-neptune</artifactId>  
       <version>${gxt.version}</version>  
     </dependency>  
     <!-- GWT -->  
     <dependency>  
       <groupId>com.google.gwt</groupId>  
       <artifactId>gwt-servlet</artifactId>  
       <version>${gwt.version}</version>  
       <scope>runtime</scope>  
     </dependency>  
     <dependency>  
       <groupId>com.google.gwt</groupId>  
       <artifactId>gwt-user</artifactId>  
       <version>${gwt.version}</version>  
       <scope>provided</scope>  
     </dependency>  
     <dependency>  
       <groupId>com.google.gwt</groupId>  
       <artifactId>gwt-codeserver</artifactId>  
       <version>${gwt.version}</version>  
       <scope>provided</scope>  
     </dependency>  
     <!-- Testing -->  
     <dependency>  
       <groupId>junit</groupId>  
       <artifactId>junit</artifactId>  
       <version>4.11</version>  
       <scope>test</scope>  
     </dependency>  
     <!-- Spring -->  
     <!--dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring</artifactId>  
       <version>${spring.version}</version>  
     </dependency-->  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-core</artifactId>  
       <version>${spring.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-context</artifactId>  
       <version>${spring.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-web</artifactId>  
       <version>${spring.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-webmvc</artifactId>  
       <version>${spring.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-orm</artifactId>  
       <version>${spring.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-context-support</artifactId>  
       <version>${spring.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-tx</artifactId>  
       <version>${spring.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-oxm</artifactId>  
       <version>${spring.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>com.thoughtworks.xstream</groupId>  
       <artifactId>xstream</artifactId>  
       <version>1.3.1</version>  
     </dependency>  
     <dependency>  
       <groupId>commons-fileupload</groupId>  
       <artifactId>commons-fileupload</artifactId>  
       <version>1.2</version>  
     </dependency>  
     <dependency>  
       <groupId>org.aspectj</groupId>  
       <artifactId>aspectjweaver</artifactId>  
       <version>1.6.8</version>  
       <scope>runtime</scope>  
     </dependency>  
     <dependency>  
       <groupId>commons-digester</groupId>  
       <artifactId>commons-digester</artifactId>  
       <version>1.7</version>  
       <type>jar</type>  
       <scope>compile</scope>  
     </dependency>  
     <!-- Hibernate -->  
     <dependency>  
       <groupId>org.hibernate</groupId>  
       <artifactId>hibernate-core</artifactId>  
       <version>${version.hibernate}</version>  
       <exclusions>  
         <exclusion>  
           <groupId>org.slf4j</groupId>  
           <artifactId>slf4j-api</artifactId>  
         </exclusion>  
       </exclusions>  
     </dependency>  
     <dependency>  
       <groupId>org.hibernate</groupId>  
       <artifactId>hibernate-annotations</artifactId>  
       <version>${version.hibernate}</version>  
     </dependency>  
     <dependency>  
       <groupId>org.hibernate</groupId>  
       <artifactId>hibernate-validator</artifactId>  
       <version>4.0.2.GA</version>  
     </dependency>  
     <!-- Driver MYSQL -->  
     <dependency>  
       <groupId>mysql</groupId>  
       <artifactId>mysql-connector-java</artifactId>  
       <version>5.1.16</version>  
     </dependency>  
     <dependency>  
       <groupId>org.hsqldb</groupId>  
       <artifactId>hsqldb</artifactId>  
       <version>2.0.0</version>  
       <scope>test</scope>  
     </dependency>  
     <!-- Driver PostgreSQL -->  
     <dependency>  
       <groupId>postgresql</groupId>  
       <artifactId>postgresql</artifactId>  
       <version>9.1-901.jdbc4</version>  
     </dependency>  
     <dependency>  
       <groupId>commons-logging</groupId>  
       <artifactId>commons-logging</artifactId>  
       <version>1.1.1</version>  
       <scope>compile</scope>  
     </dependency>  
     <dependency>  
       <groupId>commons-dbcp</groupId>  
       <artifactId>commons-dbcp</artifactId>  
       <version>1.4</version>  
     </dependency>  
     <dependency>  
       <artifactId>commons-collections</artifactId>  
       <groupId>commons-collections</groupId>  
       <version>3.1</version>  
     </dependency>  
     <dependency>  
       <groupId>commons-httpclient</groupId>  
       <artifactId>commons-httpclient</artifactId>  
       <version>3.1</version>  
     </dependency>  
     <dependency>  
       <groupId>javassist</groupId>  
       <artifactId>javassist</artifactId>  
       <version>3.4.GA</version>  
     </dependency>  
     <dependency>  
       <groupId>org.slf4j</groupId>  
       <artifactId>slf4j-log4j12</artifactId>  
       <version>${version.slf4j}</version>  
     </dependency>  
     <dependency>  
       <groupId>org.slf4j</groupId>  
       <artifactId>slf4j-api</artifactId>  
       <version>${version.slf4j}</version>  
     </dependency>  
     <dependency>  
       <groupId>log4j</groupId>  
       <artifactId>log4j</artifactId>  
       <version>${version.log4j}</version>        
     </dependency>  
     <dependency>  
       <groupId>org.eclipse.persistence</groupId>  
       <artifactId>eclipselink</artifactId>  
       <version>2.2.0</version>  
     </dependency>  
     <dependency>  
       <groupId>org.eclipse.persistence</groupId>  
       <artifactId>javax.persistence</artifactId>  
       <version>2.0.3</version>  
     </dependency>  
     <dependency>  
       <groupId>org.eclipse.persistence</groupId>  
       <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>  
       <version>2.2.0</version>  
       <scope>provided</scope>  
     </dependency>  
     <dependency>  
       <groupId>javax.annotation</groupId>  
       <artifactId>jsr250-api</artifactId>  
       <version>1.0</version>  
       <scope>provided</scope>  
     </dependency>  
     <dependency>  
       <groupId>org.codehaus.jackson</groupId>  
       <artifactId>jackson-mapper-asl</artifactId>  
       <version>1.8.1</version>  
     </dependency>  
     <dependency>  
       <groupId>javax.validation</groupId>  
       <artifactId>validation-api</artifactId>  
       <version>1.0.0.GA</version>  
     </dependency>  
     <dependency>  
       <groupId>javax.validation</groupId>  
       <artifactId>validation-api</artifactId>  
       <version>1.0.0.GA</version>  
       <classifier>sources</classifier>  
     </dependency>  
     <dependency>  
       <groupId>javax.servlet</groupId>  
       <artifactId>jstl</artifactId>  
       <version>1.2</version>  
     </dependency>  
     <dependency>  
       <groupId>javax.persistence</groupId>  
       <artifactId>persistence-api</artifactId>  
       <version>1.0</version>  
     </dependency>  
     <dependency>  
       <groupId>javax.servlet</groupId>  
       <artifactId>servlet-api</artifactId>  
       <version>2.5</version>  
       <scope>provided</scope>  
     </dependency>  
   </dependencies>  
   <repositories>  
     <repository>  
       <id>The Buzz Media Maven Repository</id>  
       <url>http://maven.thebuzzmedia.com</url>  
     </repository>  
     <repository>  
       <id>EclipseLink</id>  
       <url>http://download.eclipse.org/rt/eclipselink/maven.repo</url>  
     </repository>  
   </repositories>  
 </project>  

Y para trabajar los RPC de GWT ya no vamos a usar el típico Servlet sino que mapearemos esto con Spring, para lo cual creamos la siguiente clase:

 package com.ejemplo.sencha.server.comm;  
 import java.util.Enumeration;  
 import java.util.Properties;  
 import javax.servlet.Servlet;  
 import javax.servlet.ServletConfig;  
 import javax.servlet.ServletContext;  
 import javax.servlet.http.HttpServletRequest;  
 import javax.servlet.http.HttpServletResponse;  
 import org.springframework.beans.factory.BeanNameAware;  
 import org.springframework.beans.factory.DisposableBean;  
 import org.springframework.beans.factory.InitializingBean;  
 import org.springframework.web.servlet.ModelAndView;  
 import org.springframework.web.servlet.mvc.AbstractController;  
 /**  
  * Spring Controller implementation that mimics standard  
  * ServletWrappingController behaviour (see its documentation), but with the  
  * important difference that it doesn't instantiate the Servlet instance  
  * directly but delegate for this the BeanContext, so that we can also use IoC.*  
  */  
 public class ServletWrappingController extends AbstractController  
     implements BeanNameAware, InitializingBean, DisposableBean {  
   private Class servletClass;  
   private String servletName;  
   private Properties initParameters = new Properties();  
   private String beanName;  
   private Servlet servletInstance;  
   public void setServletClass(Class servletClass) {  
     System.out.print("setServletClass : " + servletClass);  
     this.servletClass = servletClass;  
   }  
   public void setServletName(String servletName) {  
     System.out.print("setServletName : " + servletName);  
     this.servletName = servletName;  
   }  
   public void setInitParameters(Properties initParameters) {  
     System.out.print("setInitParameters : " + initParameters);  
     this.initParameters = initParameters;  
   }  
   public void setBeanName(String name) {  
     System.out.print("setBeanName : " + name);  
     this.beanName = name;  
   }  
   public void setServletInstance(Servlet servletInstance) {  
     System.out.print("setServletInstance : " + servletInstance);  
     this.servletInstance = servletInstance;  
   }  
   public void afterPropertiesSet() throws Exception {  
     System.out.print("afterPropertiesSet");  
     if (this.servletInstance == null) {  
       throw new IllegalArgumentException("servletInstance is required");  
     }  
     if (!Servlet.class.isAssignableFrom(servletInstance.getClass())) {  
       throw new IllegalArgumentException("servletInstance [" + this.servletClass.getName()  
           + "] needs to implement interface [javax.servlet.Servlet]");  
     }  
     if (this.servletName == null) {  
       this.servletName = this.beanName;  
     }  
     this.servletInstance.init(new DelegatingServletConfig());  
   }  
   protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)  
       throws Exception {  
     System.out.print("handleRequestInternal");  
     this.servletInstance.service(request, response);  
     return null;  
   }  
   public void destroy() {  
     System.out.print("destroy");  
     this.servletInstance.destroy();  
   }  
   private class DelegatingServletConfig implements ServletConfig {  
     public String getServletName() {  
       return servletName;  
     }  
     public ServletContext getServletContext() {  
       return getWebApplicationContext().getServletContext();  
     }  
     public String getInitParameter(String paramName) {  
       return initParameters.getProperty(paramName);  
     }  
     public Enumeration getInitParameterNames() {  
       return initParameters.keys();  
     }  
   }  
 }  
Adicional a lo anterior crearemos un dispatcher con spring el cual ya debe estar definido en el web.xml y el contenido del archivo springrpc-servlet.xml:

 <?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:p="http://www.springframework.org/schema/p"  
     xmlns:aop="http://www.springframework.org/schema/aop"  
     xmlns:tx="http://www.springframework.org/schema/tx"  
     xmlns:context="http://www.springframework.org/schema/context"  
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd  
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">  
   <context:annotation-config />  
   <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">  
     <property name="urlMap">  
       <map>  
         <entry key = "GWTService">  
           <ref bean="ServiceControllerGWTService" />  
         </entry>          
       </map>  
     </property>  
   </bean>  
   <bean id="ServiceControllerGWTService" class="com.ejemplo.sencha.server.comm.ServletWrappingController">  
     <property name="servletName" value="GWTService"/>    
     <property name="servletInstance">  
       <ref bean="GWTService"/>  
     </property>  
   </bean>  
   <bean id="GWTService" class="com.ejemplo.sencha.server.GWTServiceImpl"> </bean>  
 </beans>  
Aparte de lo anterior las clases que se usan en el RPC siguen teniendo la misma estructura y ya no hace falta la notación @RemoteServiceRelativePath


Como se mencionó antes este post esta basado en el blog acerca de vaadin por lo que el modelo de datos es el mismo y los servicios spring también.

Como ya deben saber a la hora de ejecutar proyectos en GWT existe la opcion dev-mode y hosted-mode el cual explican más claramente acá. Para correr en dev-mode este proyecto en netbeans teniendo en cuenta que esta construido con maven, se hace click derecho sobre el proyecto y se escoge la opción GWT dev mode como primera instancia y luego en la consola nos sale algo como "Listening for transport dt_socket at address: 8000", luego de esto vamos al menú "Debug" y ejecutamos la opción "Attach debuger", lo que nos mostrará una ventana como la que sigue:


Si todo sale bien al darle OK después de unos momentos veremos en el navegador una ventana como la que sigue:


De nuevo el código del proyecto lo pueden encontrar en github:
https://github.com/juliandresog/SenchaGXTEjemplo.git

Visita también
https://joarchitectus.wordpress.com
https://joarchitectus.wordpress.com/2018/03/23/sencha-extjs-ajax-request-rest-json/