Mostrando entradas con la etiqueta Maven. Mostrar todas las entradas
Mostrando entradas con la etiqueta Maven. Mostrar todas las entradas

miércoles, 27 de noviembre de 2013

Tests Arquillian con Maven y Gradle

Recientemente publiqué una entrada: Aplicación CRUD con JEE7, en la que explicaba que había subido un proyecto a github: jee7-crud, con lo básico para realizar una aplicación CRUD con: JSF, EJB, JPA, JAX-RS.

Bien, acabo de actualizar jee7-crud, y le he metido herramientas básicas de testing:

  • JUnit
  • Mockito
  • Arquillian

Lo he preparado para que funcione con las dos herramientas de construcción de proyectos más populares actualmente: Maven y Gradle.

Este proyecto, A mi personalmente, me sirve de base para comenzar otros proyectos nuevos. Espero que os sea de alguna utilidad.

viernes, 5 de julio de 2013

Maven y Android

Actualización: He creado un proyecto en github (android-maven-blank): Es un proyecto plantilla con lo mínimo para que funcione con las dependencias explicadas en esta entrada.

Si te gusta tener todos tus proyectos gestionados con Maven, los proyectos de Android no iban a ser menos, pero la verdad es que Google no nos lo pone nada fácil.

A continuación voy a poner los pasos que he seguido para tener un proyecto de Android con las siguientes dependencias:

Y por supuesto, tener automatizado el proceso de generar el apk final que se subiría al play store.

Vamos a ponernos manos a la obra.

Pasos Previos

Las dependencias más complicadas de incluir van a ser precisamente las de Google, ya que no tiene un repositorio donde mantenga estos proyectos vía Maven. Por ello, lo primero que hay que hacer es descargarnos TODOS los extras del SDK Manager e instalarlos como módulos en nuestro repositorio local.



Después nos descargamos el proyecto maven-android-sdk-deployer, que se encargará de instalar todo en nuestro repositorio local. Una vez descargado nos ponemos en el directorio raíz y ejecutamos:
mvn install -P 2.2
(En mi caso pongo 2.2, porque es un SDK que tengo descargado).

Paso previo sólo si se va a utilizar Google Maps

Por lo que he visto, el proyecto maven-android-sdk-deployer despliega el módulo google-play-services, pero no lo instala correctamente porque no incluye el jar google-play-services_lib\libs\google-play-services.jar. Esto es así porque recientemente el SDK Manager ha cambiado la forma en la que distribuye este módulo. Seguro que dentro de poco el proyecto maven-android-sdk-deployer se modificará para que este paso previo no sea necesario.

Lo que he hecho para solucionarlo es instalar este jar manualmente como un módulo jar de maven:
mvn org.apache.maven.plugins:maven-install-plugin:2.4:install-file \
  -DgroupId=com.google.android.gms \
  -DartifactId=google-play-services-jar \
  -Dversion=7 \
  -Dpackaging=jar \
  -Dfile=libs/google-play-services.jar \
  -Djavadoc=google-play-services-jar-7-javadoc.jar

Paso previo sólo si se va a utilizar Google Analytics 

En caso de que vayamos a utilizar Google Analytics, tendremos que descargarnos el jar de la siguiente dirección:


y después instalar en el repositorio local el jar con la librería:
mvn org.apache.maven.plugins:maven-install-plugin:2.4:install-file
-DgroupId=com.google.android.analytics
-DartifactId=libGoogleAnalytics
-Dversion=2.0beta5
-Dpackaging=jar
-Dfile=libGoogleAnalytics.jar 

POM.XML

Copio el pom.xml que me ha quedado y lo voy comentando:


 4.0.0
 NUESTRO_GROUPID
 NUESTRO ARTIFACTID
 1.0.0-SNAPSHOT
 apk
 NUESTRO NOMBRE

 
  
   jakewharton
   http://r.jakewharton.com/maven/release/
  

  
   The mavenized Facebook Android API
   http://avianey.github.io/facebook-api-android-maven/
  

 

 

  
   com.google.android
   android
   4.0.1.2
   provided
  
  
  
   com.google.android.annotations
   annotations
   22.0.1
   provided
  
  
  

  
   com.actionbarsherlock
   actionbarsherlock
   4.3.1
   apklib
   
    
     com.google.android
     support-v4
    
   
  


  

        
            com.google.android.gms
            google-play-services
            7
            apklib
        

        
            com.google.android.gms
            google-play-services-jar
            7
        


  
  
  
   android.support
   compatibility-v13
   13
  

  
  
  
  
   com.google.android.analytics
   libGoogleAnalytics
   2.0beta5
  

  
  
  
  
   org.roboguice
   roboguice
   2.0
  

  
  
  
   com.github.avianey
   facebook-android-api
   3.0.1
   apklib
   
    
     com.google.android
     support-v4
    
   
  


  
        
            com.pivotallabs
            robolectric
            0.9.8
            test
        


        
            junit
            junit
            4.8.2
            test
        


 

 
  ${project.artifactId}
  src
        tests
  
   
    org.apache.maven.plugins
    maven-source-plugin
    2.1.2
    
     
      attach-sources
      verify
      
       jar-no-fork
      
     
    
   
   
    maven-compiler-plugin
    2.3.2
    
     1.6
     1.6
    
   
   
    com.jayway.maven.plugins.android.generation2
    android-maven-plugin
    3.6.0
    
     
      PATH_SDK
      15
     
     
      NOMBRE_EMULADOR
     
     true
                    false
    
    true
   
  

 

 
  
   sign
   
    
     
      org.apache.maven.plugins
      maven-jarsigner-plugin
      1.2
      
       
        signing
        
         sign
                                    verify
        
        package
        true
        
                                    true
         
         
          target/*.apk
         
         RUTA_KEYSTORE
         STOREPASS
         KEYPASS
         ALIAS
         
          -sigalg
          MD5withRSA
          -digestalg
          SHA1
         
        
       
      
     
     
      com.jayway.maven.plugins.android.generation2
      android-maven-plugin
      true
      
       
        false
       
                            
                                true
                                ${project.build.directory}/${project.artifactId}.apk
                                ${project.build.directory}/${project.artifactId}-signed-aligned.apk
                            
      
                        
                            
                                alignApk
                                package
                                
                                    zipalign
                                
                            
                        
     
    
   
  
 





Por supuesto que donde he puesto (PATH_SDK, NOMBRE_EMULADOR, RUTA_KEYSTORE, STOREPASS, KEYPASS, ALIAS) tendréis que poner vuestros valores.

Generar APK final para el Play Store

Una vez tenemos el pom.xml bien configurado, para generar el apk final tendremos que ejecutar:
mvn clean package -P sign
y dentro del directorio target nos generará dos apk: ${project.artifactId}.apk y ${project.artifactId}-signed-aligned.apk. El que termina en signed-aligned.apk es el que tendremos que utilizar para subir al Play Store, ya que es necesario que se le haya hecho un zip-align.



miércoles, 28 de marzo de 2012

Generar un jar a medida con diferentes paquetes de diferentes modulos de Maven

En esta entrada voy a explicar cómo montar un jar que incluya varios módulos de Maven, o eligiendo los paquetes de varios módulos.

Para ello vamos a utilizar el plugin maven-assembly-plugin. Se comporta como la mayoría de los plugins, definiendo la fase en la que queremos que se ejecute y hay que enlazar con otro fichero xml aparte donde se indique exactamente el contenido de este jar.

La forma de configurar esto dentro de nuestro pom.xml es de la siguiente manera:



maven-assembly-plugin
2.2.2


make-assembly-nuevojar

nuevojar
false

src/main/assembly/assembly-nuevojar.xml


package

single





Como podemos ver, tenemos que darle un id a esta ejecución y apuntar al fichero donde se encontrará la descripción del ensamblaje que vamos a realizar (por convención se guardan en el directorio src/main/assembly). Después indicamos que queremos que se realice después de la fase "package".

Generar jar juntando varios modulos

Para este primer caso nos vamos a meter en el contenido del fichero “assembly-nuevojar.xml”. Lo vamos a definir para que incluya las clases de 3 módulos nuestros:


xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
nuevojar

jar

false


/
true
true
runtime

com.mlopez:Module1
com.mlopez:Module2
com.mlopez:Module3





Estamos utilizando DependencySet. Con esto le indicamos que vamos a incluir en este nuevo jar el contenido de las clases de Module1, Module2 y Module3. En mi caso se encuentran bajo el mismo proyecto, y con el useProjectArtifact le indicamos que coja las de la ejecución en la que nos encontramos.

Es muy importante, que para que esto funcione las dependencias que estamos utilizando estén definidas en el pom.xml, y no vale con que estén como provided. Sino es así nos podemos encontrar con un error.

Generar jar eligiendo paquetes concretos

Para este caso vamos a generar el jar eligiendo paquetes de diferentes módulos. En mi caso, lo he hecho de una manera que no me parece nada elegante pero me funciona, siempre y cuando los módulos a coger se encuentren en el mismo proyecto que donde se va a generar el jar.


xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">

nuevojar

jar

false



../Module1/target/classes/
/

/com/mlopez/module1/package1/**/*
/com/mlopez/module1/package2/**/*



../Module2/target/classes/
/

/com/mlopez/module2/package1/**/*
/com/mlopez/module2/package2/**/*





Estamos haciendo uso del fileSet en vez del dependencySet. Con esto incluimos directamente los ficheros que queremos, en este caso las clases. Como esos modulos se encuentran dentro de nuestro mismo proyecto, ya sabemos que se encuentran bajo el directorio target/classes.

miércoles, 8 de diciembre de 2010

Personalizar proyecto según el cliente con Maven

Es un caso bastante habitual que se desarrolle un proyecto web que después vaya a ser vendido a varios clientes. En el producto final que se le venderá al cliente se tendrán que realizar una serie de personalizaciones, por ejemplo la imagén del logo, alguna css para cambiar los estilos generales de la aplicación, los valores de algunos ficheros properties (mensajes internacionalizados), etc...

Lo que considero que nunca habría que hacer es hacer una copia del proyecto y modificar los ficheros necesarios, ya que cada vez que haya que hacer un cambio en la base, también habría que hacerlo en todos los clientes.

Con maven he encontrado una manera sencilla y rápida de hacer esto, que consiste en configurarlo con profiles para que justo antes de que se genere el war final, se sobreescriban o añadan una serie de ficheros.

A continuación voy a explicar qué tipos de cosas son las que yo he necesitado personalizar y cómo hacerlo en el pom.xml de Maven.

Ficheros properties que se encuentran en el classpath de la aplicación. Generalmente esos ficheros deberían encontrarse en 'src/main/resources'. Podría interesarnos modificar algun valor concreto: Por ejemplo, imaginaros que tenemos el fichero bundle con las textos traducidos que se van a mostrar en la web, y que un texto es:

footer.text = Copyright 2010, Compañia bla bla bla bla Compañia bla bla bla.

En nuestro caso querríamos sustituir el literal "Compañia" por el nombre del cliente. Para hacer esto, lo más sencillo me parece lo siguiente: Primero sustituimos el texto footer.text con el siguiente valor:

footer.text = Copyright 2010, ${company.name} bla bla bla bla ${company.name} bla bla bla.

Nos vamos a crear un directorio nuevo 'src/main/profiles' donde vamos a crear una carpeta por cada cliente nuevo. En nuestro caso vamos a crear una carpeta 'cliente1' y ponemos en un fichero de propiedades 'filters.properties' el valor de la propiedad company.name.

company.name=Cliente1

Ahora tenemos que configurar en el pom.xml para que en los ficheros properties se filtren las propiedades y se sustituyan por los valores concretos y también habrá que crear un profile nuevo para que cuando lo utilicemos tenga en cuenta que los valores de las propiedades se encuentran en el fichero 'src/main/profiles/cliente1/filters.properties'



.....


src/main/resources

**/*.properties

true


.....

......


cliente1


src/main/profiles/cliente1/filter.properties






Ejecutando 'mvn clean package -P cliente1' ya tendremos en nuestro fichero de propiedades el valor correcto para el footer.text.

Supongamos que este modo de trabajar no nos vale porque lo que queremos hacer es sobreescribir el fichero de propiedades entero. En este caso yo dejaría los ficheros finales en el directorio 'src/main/profiles/cliente1/resources' y configurar dentro del profile para que se sobreescriban.


......


org.apache.maven.plugins
maven-resources-plugin
2.4.3

ISO-8859-1
true



......


cliente1


src/main/profiles/cliente1/filter.properties



src/main/profiles/cliente1/resources

**/*.properties

true







La primera parte donde se define el plugin 'maven-resources-plugin' es muy importante, ya que hay que definir la configuración '<overwrite>true</overwrite>' porque sino los ficheros no se sobreescribirán.

Otra cosa que podemos querer sobreescribir son las imagenes, css y contenido estático que se encuentre en el directorio 'src/main/webapp'.

Para ello colgamos los ficheros que queramos sobreescribir en el directorio 'src/main/profiles/cliente1/webapp' manteniendo dentro la misma estructura de directorios que se sigue en 'src/main/webapp'. Y después lo único que nos falta sería configurar maven para que nos copie esos ficheros. Esto último es un poco más complicado que como lo hemos hecho hasta ahora porque no se puede utilizar el tag <resources> ya que siempre nos lo copiará dentro del directorio WEB-INF/classes. Si queremos definir un nuevo directorio lo tenemos que hacer de la siguiente manera:



cliente1


src/main/profiles/cliente1/filter.properties



maven-resources-plugin
2.4.3

ISO-8859-1
true



copy-personalization
process-resources

copy-resources


${basedir}/target/${project.artifactId}-${project.version}


src/main/profiles/cliente1/webapp











Si ejecutamos 'mvn clean package -P cliente1' deberían estar todos los ficheros sobreescritos, pero no es así. ¿Por qué? La razón es que en la fase 'process-resources' en la que se está ejecutando la tarea 'copy-personalization' no se ha creado todavía el directorio 'target/${project.artifactId}-${project.version}'. La única forma posible que he encontrado de solucionar este problema es indicarle a maven que adelante la creación de este directorio mediante el goal 'war:exploded'. Por lo que la ejecución de Maven final que funciona es:

mvn clean war:exploded package -P cliente1

jueves, 21 de octubre de 2010

Reemplazar textos con Maven

En el trabajo he tenido que mostrar la versión actual del producto en el footer de la página.

Lo normal en este caso creo que sería tener un fichero de propiedades en algún sitio de la aplicación y desde el footer.jsp mostrar el valor de esa propiedad. Para que cuando generamos el war de la aplicación automaticamente en ese .properties aparezca la versión utilizada en el pom.xml nos podemos valer fácilmente de:




src/main/resources

**/*.properties
**/*.xml
**/*.xmls
**/*.sql

true




Y dentro del fichero .properties tener una propiedad que sea "version=${pom.version}".

Pero la verdad es que no lo he hecho así. Me ha dado pereza crearme un fichero de propiedades sólo para mostrar la versión.

¿Por qué no se puede hacer el filtering de los resources sobre todas las jsps? Indicándole que el sería "src/main/java/WEB-INF/jsp". Así lo pensé yo y cuando se me generó el war y lo desplegué me llevé una desagradable sorpresa.

En las jsps estabamos utilizando expresiones EL, que tienen la misma nomenclatura que las variables de maven: ${...}. Y al realizar el filtering de Maven, se me estaba modificando el valor de algunas variables EL.

Debo reconocer que llegado a este punto debería haber vuelto a la idea inicial, pero siguió dándome pereza e intenté investigar otra forma de realizar sustituciones de tokens concretos y llegué a este interesante plugin: maven-replacer-plugin.

En footer.jsp puse el siguiente código:

version: PROJECT_VERSION

y en maven configuré el plugin:




com.google.code.maven-replacer-plugin
maven-replacer-plugin
1.3.2



project_version

prepare-package

replace




target/${project.artifactId}-${project.version}/WEB-INF/**/*.jsp

false

PROJECT_VERSION

${pom.version}







De esta forma le indicamos que busque en las jsps el token exacto 'PROJECT_VERSION'.

Sólo hay que tener en cuenta una cosa más para que esto funcione. en el momento en el que se ejecuta este plugin el directorio de la aplicación todavía no se encuentra en el directorio target, por lo que no se realiza bien el replace si se ejecuta la tarea 'mvn clean package'. Es necesario ejecutar 'mvn clean war:exploded package'

jueves, 15 de abril de 2010

Excluir clases al obtener la cobertura de tests

Considero que saber en todo momento el porcentaje (cobertura) de nuestro código que está siendo testeado por nuestras pruebas unitarias es algo muy util para saber la calidad con la que ha sido desarrollado.

También me parece que hay que intentar mantener que este porcentaje nunca baje de un límite (por ejemplo 80%).

Si se utiliza Scrum creo que es un elemento que hay que comprobar en las finalizaciones de todos los Sprints y tenerlo muy en cuenta en las retrospectivas para saber si vamos por buen camino o no.

También se puede llegar a indicarle a Maven que si el porcentaje de tests está por debajo de un limite ni siquiera llegue a compilarse el paquete y genere un error.

Recientemente nos hemos encontrado con un problema en el trabajo, y es que nuestra cobertura estaba bajando debido a unas clases que no pueden ser testeadas o en las que no se debería perder tiempo haciendo tests tontos:

- Entidades: ¿Realmente es necesario que todos los getter/setters de un bean se testeen si lo unico que hacen es devolver y settear un valor? ¿Acaso no confiamos en la generación automática de get/set del Eclipse? Yo personalmente no perdería el tiempo testeando estas clases.

- Stubs de Web services: Supongamos que tenemos unos stubs generados a partir de un WSDL. Me niego rotundamente a tener que testear estas clases.

Seguramente que habrá mil casos mas de clases que no es necesario testear y que mucha gente se planteará hacerlo para que la cobertura del proyecto no baje.

En vez de hacer malabarismos, hay una solución muy sencilla que es indicarle al plugin maven de cobertura que excluya las clases que queramos:



org.codehaus.mojo
cobertura-maven-plugin



com/company/models/*.class






clean







Con esta configuración le estamos indicando a cobertura que pase de todos mis modelos, que son los que tienen los get/set :-)

domingo, 14 de marzo de 2010

Ofuscar código Java con Maven

Recientemente he tenido que configurar un plugin en Maven para que automáticamente genere los jars ofuscados.

La solución que voy a proponer se basa en el proyecto Yguard. También existe Proguard que se integra bien con Maven aunque, para las necesidades que tenía, se ajustaba mejor el primero.

La configuración del plugin que hay que añadir al pom.xml de Maven es la siguiente:




maven-antrun-plugin


yguard
yguard
2.3.0.1




package







in="${project.build.directory}/${project.build.finalName}.${project.packaging}"
out="${project.build.directory}/${project.build.finalName}.jar" >

replaceClassNameStrings="true">



















run







Como se puede ver, este plugin se ejecuta en la fase 'package' de Maven, justo antes de generar el jar final.

No todo el código debe ser ofuscado. Esto lo tenemos que tener en cuenta sobre todo si estamos desarrollando una librería o unos beans de Spring que tienen que ser accedidos desde otros proyectos. Por ello hay que indicarle a YGuard que no ofusque las partes definidas como públicas en el código.

Si estamos trabajando con JPA y con beans definidos como entidades (@Entity) nos podemos encontrar con la desagradable sorpresa de que los atributos que tiene definidos como Columnas (@Column) son privados y por ello también se han ofuscado. Esto quiere decir que si vamos a la base de datos veremos que, en vez de los nombres de columnas que habíamos definido inicialmente, se han creado columnas en las tablas con nombres como A,B,C,D,etc...

No he encontrado una manera muy elegante para solucionar esto. Lo ideal sería especificarle a YGuard que no ofusque aquellas clases que tengan la anotación @Entity, pero no he encontrado ninguna configuración que lo permita. Si alguien sabe cómo hacer esto agradecería que lo comentara :). En cambio lo que sí se puede especificar es que no ofusque aquellas clases que acaben con Entity.

En nuestro equipo hemos llegado a la convención de que todas las clases que sean entidades terminen con el nombre Entity: (UserEntity, RoleEntity, etc...)

la configuración para especificar que no ofusque estas clases viene en la sección keep: