This is the presentation we gave at Spring 2GX Madrid. It shows how Grails helped us to improve our productivity and why Grails is not that bounded to Groovy and how it can be an outstanding alternative if you are a 100% Java company.
Resistencia extrema al cobre por un consorcio bacteriano conformado por Sulfo...
Jobsket Spring 2GX Madrid
1. Productividad máxima con Grails y Java
“Because you're worth much more” way
Daniel Latorre – Jordi Monné – Martín Pérez / Jobsket S.L.
Haga clic para modificar el estilo de subtítulo del patrón
2/21/10
2. Sobre Nosotros
● Martin Pérez: Javero desde hace 10 años.
Ultimos años como senior architect/contractor
en la industria financiera en Irlanda.
● Daniel Latorre: Programador Java, Groovy,
Rails, Php. Google Summer of code donde
contribuyó a Grails.
● Jordi Monné: Nuestro Spring expert. Javero
profesional. ex-JavaConGanas
2/21/10
3. Índice
● Acerca de Jobsket
● Jobsket y Grails: Una historia de amor y odio
● Integrando Grails y Java
● Lo que nos gusta más de Grails
2/21/10
6. ¿Y todo esto por qué os lo contamos?
● Porque somos sólo tres personas.
● Que hemos hecho una aplicación técnicamente superior a
la mayoría de sitios web de empleo.
● Que hemos podido realizar innovaciones que nuestros
clientes no se creen cuando las ven.
● Que tenemos un producto sólido que actualizamos
frecuentemente en producción, casi cada semana.
● Y porque seguramente Grails tenga algo que ver.
2/21/10
7. Lucene
Arquitectura
Index
Lucene LuceneLucene
Index Index Index
Scripts
OS Infojobs
Integration Storage Search
Crawlers
Customer Controllers
Website
Stats
Views
Infoempleo
Customer
Website
Spring Framework
RedTrabaja
Customer
Website
Tecnoempleo
GORM Hibernate
Spain Ireland Master
2/21/10
8. Cifras
● Tomcat + Grails 1.0.5 ● 60.000 lineas de código
● 3 Instancias de MySQL ● 70% Java
● Un índice de Compass ● 20 Crawling threads
● 10 índices de Lucene ● 350 GSPs
● 1Gb Heap. 1 Major GC
cada 24 horas.
2/21/10
10. ¿Por qué Grails?
● 2008. Startup. Inseguridad. ¿Funcionará?
● Al menos escojamos algo nuevo para aprender
● Pero tiene que ser algo con lo que estemos familiarizados
● Y que más o menos se vea que las cosas van por ahí
● ¿RoR? : No tenemos ni idea de Ruby
● WebFlow: Lo mismo de siempre
● ¿Grails? Venga, probamos.
2/21/10
11. Escenario
● Tres personas core Java.
● Legacy de algún otro proyecto en Java.
● Sistemas complejos ya desarrollados en Java.
● Montones de librerías ya existentes en Java
que podríamos reutilizar.
● Así que vamos con Grails
2/21/10
12. Las cosas no fueron tan bonitas
● Grails todavía en betas
● Tentados a echar marcha atrás
● Muy buggy hasta la versión 1.0.3
● Muchos plugins que no funcionaban
● Soporte en IDEs inexistente no sólo en cuanto
a sintaxis sino usabilidad (crash cada 30mins)
2/21/10
13. Las cosas no fueron tan bonitas
● Quartz Plugin. Problemas con la sincronización
de sesiones.
● GORM y Grails están pensados para utilizar una
única conexión de BD.
● Servicios que acceden a diferentes BDs = problema
● Algún plugin pero no funcionaba bien
2/21/10
14. Pero por otra parte
● Plataforma enormemente sencilla e intuitiva.
● La integración con Spring estaba muy bien.
● Adicción a:
● La plataforma de testing
● La partición en environments
● La cantidad de plugins. Algunos funcionaban bastante
bien :) (ahora son mucho más estables)
● La comunidad
2/21/10
16. Así que seguimos con Grails
● Pero. Casi todo nuestro código está en Java
● Sólo utilizamos Groovy en:
● Controllers
● TagLibs
● Tests
● Filters
● Maximizar la productividad: Nuestro
conocimiento en Java + Plataforma Grails
2/21/10
17. Grails y Java
● Groovy no es excusa para no usar Grails.
● No es que no nos guste Groovy, pero:
● Es difícil encontrar perfiles en Groovy.
● Las empresas tienen mucho talento en Java.
● Hay muchas librerías ya en Java.
● El soporte de herramientas es muy malo.
● Sin embargo hemos encontrado que Groovy es
muy bueno para algunas cosas.
2/21/10
18. ¿Me interesa para mi empresa?
● Muy productivo
● Reaprovechamiento de nuestro legacy Java
● Fácil partición de equipos:
● Los que le gusta lo dinámico
● Las viejas glorias de Java
● El núcleo en Java y la parte dinámica en
Groovy. A nosotros nos ha funcionado bien.
2/21/10
20. Integración de Grails y Java
● Integramos Grails con
● Hibernate, Spring, Lucene, Compass, Maven, Hudson, ...
● No utilizar Groovy en los servicios no implica
perder productividad
● La clave, la plataforma Grails
2/21/10
21. Integración de Grails y Java
● Integración con Spring
● Capa de servicio completamente en Java
● Configuración típica en resources.xml
● Perdemos la posibilidad de enlazar dependencias en
runtime
● Sencilla y transparente
● Exactamente igual que cualquier otro proyecto no
Grails
2/21/10
22. Integración de Grails y Java
● Integración con Spring
● grails-app/conf/spring/resources.xml
<beans xmlns....>
<import resources=”crawlers.xml”/>
<bean id="jobOffersService" class="com.jobsket.services.JobOfferService">
<property name="configuration" ref="freemarkerMailConfiguration" />
<!-- otras dependencias -->
</bean>
</beans>
2/21/10
23. Integración de Grails y Java
● Integración con Spring
● grails-app/controllers/JobOffersController.groovy
class JobOffersController = {
def jobOffersService ID del bean de Spring
def index = {
def jobs = jobOffersService.lastJobOffers();
jobs.sort{it.date}
[jobs:jobs]
}
}
2/21/10
24. Integración de Grails y Java
● Integración con Spring
● grails-app/controllers/JobOffersController.groovy
class JobOffersController = {
def jobOffersService
Método del servicio inyectado
def index = {
def jobs = jobOffersService.lastJobOffers();
jobs.sort{it.date}
[jobs:jobs]
}
}
2/21/10
25. Integración de Grails y Java
● Integración con Spring
● grails-app/controllers/JobOffersController.groovy
class JobOffersController = {
def jobOffersService
def index = {
def jobs = jobOffersService.lastJobOffers();
jobs.sort{it.date} Groovy :)
[jobs:jobs]
}
}
2/21/10
26. Integración de Grails y Java
●
● Integración con Hibernate
● Modelo mixto
● GORM en algunos casos
● Hibernate con HibernateDaoSupport en otros
● Se mezclan perfectamente
● Necesitabamos múltiples DataSources
● El plugin no nos funcionaba demasiado bien
2/21/10
27. Integración de Grails y Java
●
● Integración con Hibernate
● Configuración para GORM
dataSource {
pooled = true
driverClassName = "com.mysql.jdbc.Driver"
username = "tu_usuario"
password = "tu_password"
}
2/21/10
28. Integración de Grails y Java
●
● Integración con Hibernate
● Configuración no GORM
<bean id="masterDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
depends-on="mysqlJDBCDriver"
destroy-method="close">
<property name="jdbcUrl" value="jdbc:mysql://${master.db.server}:3306/jobsket_master"/>
<property name="user" value="tu_usuario"/>
<property name="password" value="tu_password"/>
</bean>
2/21/10
29. Integración de Grails y Java
●
● Integración con Hibernate
● Mappings en *.hbm.xml sea para GORM o sin GORM
<hibernate-mapping package="com.jobsket.core.model">
<class name="Role" table="roles">
<id name="id" type="integer">
<column name="id" />
<generator class="identity" />
</id>
<property name="name" type="string" not-null="true" length="64" />
...
</class>
2/21/10
30. Integración de Grails y Java
●
● Integración Spring-Groovy
● Reutilización de variables *.groovy en la configuración Spring
● Configuración dinámica
● Setear variables en función del environment
● Condicionales
2/21/10
31. Integración de Grails y Java
●
● Integración Spring-Groovy
● Fichero Spring XML
<bean id="masterDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
depends-on="mysqlJDBCDriver"
destroy-method="close">
<property name="jdbcUrl" value="jdbc:mysql://${master.db.server}:3306/jobsket_master"/>
<property name="user" value="tu_usuario"/>
<property name="password" value="tu_password"/>
</bean>
2/21/10
32. Integración de Grails y Java
●
● Integración Spring-Groovy
● grails-app/conf/Config.groovy
development { 1 = variable según environment
2 = variable según condición
master.db.server=”localhost" (1)
}
production {
locale = System.getProperty("jobsket.locale")
If (locale.equals("es")) { crawlers.ip=crawlers_es } else { crawlers.ip=crawlers_ie } (2)
master.db.server=db_production (1)
}
2/21/10
33. Integración de Grails y Java
● Integración con Spring Web Flow
● Ideal para crear flujos entre páginas
● Reserva billete de avión, formulario de pago, ...
● Plugin Grails WebFlow
● Facilidad de uso con su DSL
● Añadir o quitar flujos sin reiniciar, genial
2/21/10
36. Integración de Grails y Java
● Integración con Spring Web Flow
def registerFlow = {
recruiterDetails {
on("next") { ... }.to "payment"
on("cancel").to "cancel"
}
payment {
on("nextCreditcardTab") { ... }.to "confirm"
on("nextBankTransferTab") { ... }.to "confirm"
}
2/21/10
37. Integración de Grails y Java
● Integración con Spring Web Flow
def registerFlow = {
recruiterDetails {
on("next") { ... }.to "payment" click en next
on("cancel").to "cancel"
}
payment {
on("nextCreditcardTab") { ... }.to "confirm"
on("nextBankTransferTab") { ... }.to "confirm"
}
2/21/10
38. Integración de Grails y Java
● Integración con Compass
● Proyecto para integrar búsquedas full-text
● Plug-in Searchable
● No lo utilizamos
● Nuestras clases de modelo són Java
● Integración como en cualquier otro proyecto Java
2/21/10
39. Integración de Grails y Java
● Integración con Compass
● @Searchable en las entidades a indexar/buscar
● Configuración en grails-app/conf/compass/compass.cfg.xml
● CompassService.java
CompassConfiguration configuration = new CompassAnnotationsConfiguration().
configure("/compass/compass.cfg.xml")
compass = configuration.buildCompass();
2/21/10
40. Integración de Grails y Java
● Integración con Hudson
● Servidor de Integración Continua
● Hudson con plugin de Grails
● Hudson sin plugin de Grails
● Nuestra elección (ninguna razón especial)
● Muy fácil de configurar para trabajar con Grails
2/21/10
43. Integración de Grails y Java
● Integración con Maven
● Herramienta para gestionar y construir proyectos Java
● Utilizamos el plugin para Maven
● Se combina bien con los goals por defecto
● mvn clean, mvn compile
● Añade nuevos goals
● mvn grails:run-app, grails:list-plugins
2/21/10
44. Integración de Grails y Java
● Integración con Maven
● Administración de dependencias con pom.xml
<dependencies>
<dependency>
<groupId>org.grails</groupId>
<artifactId>grails-crud</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
2/21/10
49. Grails UI
● Pros:
● Componentes precocinados sencillos de utilizar.
● Aspecto adaptable(vía css)
● Se cargan sólo las dependencias js necesarias
● Contras:
● Genera código js junto al html
● Dificultad para añadir/adaptar algunos comportamientos a
los componentes.
2/21/10
50. Taglibs
● Reutilizar lógica de presentación
● Mucho más simple que con JSP(closure en una clase
groovy)
● Repetimos menos código en los controllers y es más
potente que templates GSP
● Posibilidad de hacer tests
● Es posible reutilizarlos desde controllers y otros tags
2/21/10
51. Testing
● Uno de los puntos fuertes de Grails.
● Cubre todos los tipos de tests:
● Unitarios
● Integración
● Funcionales(vía plugins)
● Plugins para testing(Spock, dbUnit, Code
Coverage, Easyb, Webtest...)
2/21/10
52. Tests de integración
● Hacer tests de integración es complejo.
● Hay que preparar los sistemas, las bases de datos, los
ficheros.
● Hay que gestionar la inicialización de recursos. Inicializar
Spring, Hibernate, preparar y rellenar las bases de datos...
● Mucha gente termina haciendo su framework.
2/21/10
53. Test de un Service
class CvServiceTests extends GroovyTestCase {
Beans inyectados desde Spring
def cvService
void testFindById() {
def cv = new CV(id:1,name:"Dani")
cvService.save(cv) Accesos reales a BD
cv = cvService.findById(1)
assertNotNull cv
assertEquals "Dani", cv.name
}
}
2/21/10
54. Tests para los Controllers
● Es posible hacer tanto tests unitarios como de integración.
● Ejecutar acciones y comprobar el estado del controlador.
● Utiliza Spring Mock (MockHttpServletRequest,
MockHttpServletResponse y MockHttpSession).
2/21/10
55. Test de un Controller
class JobOfferControllerTests extends GroovyTestCase {
Beans inyectados desde Spring
def jobOffersService
void testCreateJobOffer() {
def controller = new JobOffersController()
controller.jobOffersService = jobOffersService
controller.params.title = "Groovy Developer"
controller.params.city = "Madrid"
Ejecuta la action
controller.save()
assertEquals "/joboffer/index", controller.response.redirectedUrl
def jobOffers = jobOffersService.findAllJobOffers() Comprueba la BD
assertEquals 1, jobOffers.size()
assertEquals "Madrid", jobOffers[0].city
}
}
2/21/10
56. Test Funcionales (Webtest)
class AuthWebtest extends grails.util.WebTest {
def testLogin(){
invoke '/login'
verifyText 'Entra en Jobsket'
setInputField(name:'login',value:"dani")
setInputField(name:'password',value:"dani")
clickButton 'Entra'
verifyText 'Publica tu CV'
}
}
● Alternativas: Functional Testing, Selenium, Webdriver...
2/21/10
58. Groovy: Modificar métodos
● Sin necesidad de extender una clase para un
mock puntual
cvService.metaClass.getOriginalInputStream = { int id->
return new MockMultipartFile("test.pdf", new byte[0])
.getInputStream()
}
2/21/10