Jobsket Spring 2GX Madrid
-
Upload
jobsket -
Category
Technology
-
view
2.582 -
download
0
description
Transcript of Jobsket Spring 2GX Madrid
Haga clic para modificar el estilo de subtítulo del patrón
2/21/10
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.
2/21/10
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
Í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
2/21/10
DEMO
2/21/10
¿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
Arquitectura
IrelandSpain Master
Infojobs
Tecnoempleo
Infoempleo
RedTrabaja
OS Integration
Lucene Index
Lucene Index
Lucene Index
Lucene Index
Customer Website
Customer Website
Customer Website
Control lers
Views C
raw
lers
Stat
s
Spring Framework
GORM Hibernate
Storage Search
Scripts
2/21/10
Cifras
● Tomcat + Grails 1.0.5
● 3 Instancias de MySQL
● Un índice de Compass
● 10 índices de Lucene
● 1Gb Heap. 1 Major GC cada 24 horas.
● 60.000 lineas de código
● 70% Java
● 20 Crawling threads
● 350 GSPs
2/21/10
2/21/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
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
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
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
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
¿Comunidad?
2/21/10
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
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
¿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
2/21/10
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
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
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
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}
[jobs:jobs]
}
}
ID del bean de Spring
2/21/10
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}
[jobs:jobs]
}
}
Método del servicio inyectado
2/21/10
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}
[jobs:jobs]
}
}
Groovy :)
2/21/10
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
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
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
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
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
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
Integración de Grails y Java●
● Integración Spring-Groovy● grails-app/conf/Config.groovy
development {
master.db.server=”localhost"
}
production {
locale = System.getProperty("jobsket.locale")
If (locale.equals("es")) { crawlers.ip=crawlers_es } else { crawlers.ip=crawlers_ie }
master.db.server=db_production
}
(1)
(1)
(2)
1 = variable según environment2 = variable según condición
2/21/10
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
Integración de Grails y Java
2/21/10
Integración de Grails y Java
2/21/10
Integración de Grails y Java
● Integración con Spring Web Flowdef registerFlow = {
recruiterDetails {
on("next") { ... }.to "payment"
on("cancel").to "cancel"
}
payment {
on("nextCreditcardTab") { ... }.to "confirm"
on("nextBankTransferTab") { ... }.to "confirm"
}
2/21/10
Integración de Grails y Java
● Integración con Spring Web Flowdef registerFlow = {
recruiterDetails {
on("next") { ... }.to "payment"
on("cancel").to "cancel"
}
payment {
on("nextCreditcardTab") { ... }.to "confirm"
on("nextBankTransferTab") { ... }.to "confirm"
}
click en next
2/21/10
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
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
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
Integración de Grails y Java
2/21/10
Integración de Grails y Java
2/21/10
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
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
2/21/10
Lo que más nos gusta
● Plugins
● Taglibs
● Testing
● El dinamismo de Groovy
2/21/10
Plugins
● Image tools
● Avatar
● Webtest
● JSecurity
● Grails UI (basado en Yahoo UI)
2/21/10
Componentes de Grails UI
2/21/10
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
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
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
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
Test de un Service
class CvServiceTests extends GroovyTestCase { def cvService void testFindById() { def cv = new CV(id:1,name:"Dani") cvService.save(cv) cv = cvService.findById(1)
assertNotNull cv assertEquals "Dani", cv.name } }
Accesos reales a BD
Beans inyectados desde Spring
2/21/10
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
Test de un Controller
class JobOfferControllerTests extends GroovyTestCase { def jobOffersService void testCreateJobOffer() { def controller = new JobOffersController() controller.jobOffersService = jobOffersService controller.params.title = "Groovy Developer" controller.params.city = "Madrid" controller.save() assertEquals "/joboffer/index", controller.response.redirectedUrl
def jobOffers = jobOffersService.findAllJobOffers() assertEquals 1, jobOffers.size() assertEquals "Madrid", jobOffers[0].city } }
Beans inyectados desde Spring
Ejecuta la action
Comprueba la BD
2/21/10
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
Groovy: Añadir métodos
● Código más limpio
StringBuilder.metaClass.appendNotNull = { str -> if(str){ append(str) }}...def sb = new StringBuilder()sb.appendNotNull(unaCadena)sb.appendNotNull(otraCadena)...if(unaCadena){ sb.append(unaCadena)}if(otraCadena){ sb.append(otraCadena)}
2/21/10
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
Groovy: Closures
● Modificar un tag estándar de Grails
def applicationTaglib = ctx.getBean("org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib")
def createLinkJobsket = { attrs, body-> ...}createLinkJobsket.delegate = applicationTaglibcreateLinkJobsket.resolveStrategy = Closure.DELEGATE_FIRSTapplicationTaglib.createLink = createLinkJobsket
2/21/10
GRACIAS
2/21/10
QA