viernes, 25 de octubre de 2013

Aplicación CRUD con JEE 7

A continuación os voy a compartir el código de una simple aplicación web que utiliza JEE7 y se despliega en un Glassfish 4.

Descargar código fuente

El código está basado en la siguiente entrada de Arun Gupta. Concretamente el código original se encuentra aquí. Recomiendo la lectura de la entrada entera. Me parece que hace una buena comparación entre Spring y JEE6, aunque personalmente creo que se equivoca en un aspecto. En uno de los puntos hace una comparación de rendimiento entre Glassfish+JEE6 y Glassfish+Spring. Creo que lo más fiel a la realidad sería utilizar Tomcat+Spring, ya que la aplicación programada con Spring no va a utilizar gran parte de las funcionalidades del Glassfish.

El código original viene con un sencillo CRUD sobre una entidad "Persona". Las tecnologías que se utilizan son: JSF, EJB, JPA.

El código fuente con mis modificaciones os lo podéis descargar aquí. He añadido JAX-RS para exponer un Servicio WEB REST a través de JSON.

Una vez que tengamos el código descargado podemos desplegarlo a través de nuestro IDE, o generar el WAR a través de maven (mvn package) o gradle (gradle war) y desplegarlo manualmente.

Para que la aplicación funcione será necesario configurar un Datasource en Glassfish. Tal y cómo se indica en el fichero "src/main/resources/META-INF/persistence.xml", la aplicación web se conecta al nombre JNDI "jdbc/myDataSource".

En este caso voy a explicar los pasos para crear un Datasource que se conecte a una base de datos Postgresql en Glassfish.

Una vez arrancado el glassfish vamos a http://localhost:4848 , Resources -> JDBC -> JDBC Connection Pools y pulsamos sobre el botón New.


En esta pantalla rellenamos los siguientes campos:

  • Pool Name: MyDatabase. Ponemos el nombre de nuestra base de datos, o el nombre que más rabia nos de.
  • ResourceType: javax.sql.ConnectionPoolDatasource
  • Database Driver Vendor: Postgresql

y pulsamos sobre Next. La parte superior de la siguiente pantalla tiene que quedar como en la siguiente imagen. No tocamos nada.



En la parte inferior tendremos que configurar las siguientes propiedades de conexión con nuestra base de datos:
  • User
  • DatabaseName
  • Password
  • ServerName
  • PortNumber. El puerto por defecto del postgresql es 5432


Al pulsar sobre "Finish" ya tenemos nuestro pool creado. Ahora nos falta crear la entrada JNDI que conecte con el pool que hemos creado. Para ello vamos a Resources -> JDBC -> JDBC Resources



Creamos un nuevo Resource pulsando sobre el botón "New" y configuramos los siguientes campos:
  • JNDI Name: jdbc/myDatasource
  • Pool Name: Elegimos el nombre del pool que acabamos de crear.


Una vez configurado el Datasource la aplicación web ya debería poder desplegarse y funcionar perfectamente.

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.

jueves, 31 de marzo de 2011

Conectar 2 bases de datos con JPA

Leyendo sobre el tema he visto que esto es algo tan sencillo como duplicar los beans entityManager, entityManagerFactory y datasource con la información de conexión a la base de datos nueva. También habría que generar un nuevo persistence-unit en el fichero persistence.xml.

Después en la aplicación, suponiendo que inyectamos el EntityManager a nuestros DAOs mediante la anotación @PersistenceContext, a cada uno de ellos habría que incluirle el atributo unitName especificado en el persistence.xml:

@PersistenceContext(unitName="defaultDB")

En mi caso esto no me ha valido porque en el momento de tener que conectar otra base de datos a mi aplicación, ésta importaba una serie de librerías comunes que utilizamos para más proyectos en los que había objetos DAO con un @PersistenceContext sin especificar ningún unitName. Cambiar la librería común para que especifira el unitName es inviable ya que en cada aplicación en la que se utiliza su valor es diferente.

Al arrancar la aplicación sin hacer nada la excepción que aparece es:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [javax.persistence.EntityManagerFactory] is defined: expected single bean but found 2
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findDefaultEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:536)

Esta claro. El DAO que tiene el @PersistenceContext no sabe qué entityManager tiene que recibir y no podemos modificarlo para especificárselo.

En la excepción se puede ver que el error ocurre en el método findDefaultEntityManagerFactory de la clase PersistenceAnnotationBeanPostProcessor de Spring. Es el encargado de inyectar el entityManager a los @PersistenceContext.

Me he creado una clase que hereda de PersistenceAnnotationBeanPostProcessor y sobreescribe el método findDefaultEntityManagerFactory de tal manera que voy a devolver el entityManagerFactory que yo quiera. La clase en cuestión quedaría así:



public class PersistenceAnnotationBeanPostProcessor extends org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor{

private EntityManagerFactory defaultEntityManagerFactory;

protected EntityManagerFactory findDefaultEntityManagerFactory(String requestingBeanName)
throws NoSuchBeanDefinitionException{
if (defaultEntityManagerFactory!=null){
return defaultEntityManagerFactory;
}
return super.findDefaultEntityManagerFactory(requestingBeanName);
}

public void setDefaultEntityManagerFactory(
EntityManagerFactory defaultEntityManagerFactory) {
this.defaultEntityManagerFactory = defaultEntityManagerFactory;
}

}



Lo único que tendríamos hacer a continuación es definir un bean en la configuración de Spring con esta clase en vez de la que utiliza Spring por defecto:









Eso sí, tenemos que especificar en el atributo "defaultEntityManagerFactory" qué entityManager queremos que se inyecte cuando nos encontramos con un @PersistenceContext sin especificar un unitName.

La conexión a la segunda base de datos yo sólo la he utilizado para realizar consultas sobre ella, nunca para realizar modificaciones. No sé si habría algún fallo en las transacciones, ya que el JpaTransactionManager está ligado únicamente a un EntityManagerFactory.

lunes, 28 de marzo de 2011

Define custom mapping for namespace to package

En esta entrada voy a comentar una cosa bastante simple.

Cuando queremos crear un cliente de servicio web desde eclipse llegamos a un punto en el que podemos configurar un mapeo de paquetes, para que no se creen los stubs automáticamente en el paquete que le corresponda segun el namespace que venga indicado en el wsdl, sino en el sitio en el que nosotros queramos.

Esto se consigue marcando la opción "Define custom mapping for namespace to package".



Para no tener que repetir la tarea de indicar el namespace y el paquete al que queremos que se mapee, podemos crearnos un fichero de propiedades y darle al botón "Import".



Tenemos que tener en cuenta que en este fichero no podemos poner la ruta del namespace tal cual, ya que los dos puntos no lo reconoce. Lo tenemos que poner en utf8. Ejemplo:

http\u003A//namespace.marcos.com=com.marcos.ws.stubs
http\u003A//namespace.sub.dominiomarcos.com=com.marcos.ws.stubs

miércoles, 19 de enero de 2011

Propagar atributos en tiles

Una vez que estamos configurando nuestra plantilla de tiles mediante xml se nos puede presentar la necesidad de utilizar atributos dentro de una jsp que se está visualizando mediante un <tiles:insertAttribute />. Voy a poner un ejemplo para que se entienda mejor el caso:

En nuestro fichero tiles-def.xml tenemos el siguiente definition:












El main.jsp tiene el siguiente contenido:


<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles"%>
....





Hasta aqui nada raro. Pero, ¿Qué pasa si dentro del header queremos utilizar el atributo menu? header.jsp:


<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles"%>

<img src="<%=request.getContextPath()%>/images/logo.png" />



Si ejecutamos esto nos dara una excepción NoSuchAttributeException, quejándose de que no existe el atributo 'menu'. ¿Qué soluciones tenemos?

La primera opción que se me ocurre y seguramente sea la más elegante es reordenar un poco la definición que tenemos dejándola de la siguiente manera:














Y el main.jsp quedaría:


<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles"%>
....





Si esta solución no os convence y no podeis separar la definición existente en dos (como me ha ocurrido recientemente) se puede hacer lo siguiente:

Primero, exponer todos los atributos de la definición como atributos de la request dentro del main.jsp:


<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles"%>
....








Después modificamos el header para que, en vez de utilizar el <tiles:insertAttribute/>, se use el <jsp:include/>:


<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles"%>

<img src="<%=request.getContextPath()%>/images/logo.png" />



No me gusta nada esto de tener que utilizar el requestScope y jsp:includes directamente y en mi opinión tiene que haber otra forma de hacerlo más
elegante, pero estuve investigando un poco y no encontré nada. Como esto funciona no le dí muchas más vueltas jeje.

viernes, 31 de diciembre de 2010

Crear estructura de proyecto inicial con Spring Roo

Una de las cosas que más tiempo lleva cuando queremos empezar un nuevo proyecto Java es la creación de la estructura inicial: Estructura inicial de directorios, importar librerías, configurar frameworks (spring, mvc, persistencia, etc...).

Una posible solución rápida para esto puede ser utilizar Spring Roo.

Resumiendo un poco, Spring Roo es una herramienta que te proporciona una consola mediante la que puedes ejecutar comandos para ir añadiendo funcionalidades a tu aplicación. Por ejemplo, con un solo comando te añade todo lo necesario para conectarte mediante JPA a una base de datos (librerías necesarias, configuración de Spring, etc...). También te permite crear entidades fácilmente y por cada una de ellas te genera un CRUD automáticamente. Si en este punto quieres saber más sobre esta herramienta te recomiendo un webinar de aproximadamente una hora que impartió ecamacho: Webinar sobre Spring-Roo.

En mi caso voy a utilizar Spring Roo para que me cree la estructura inicial del proyecto, que me configure la capa de la persistencia con Hibernate y PostgreSQL, y que me añada las dependencias necesarias para funcionar con Spring MVC.

A continuación explico los pasos necesarios:

Primero nos descargamos Spring-Roo de la siguiente dirección: http://www.springsource.org/download. Una vez lo tengamos descargado y descomprimido, nos creamos un directorio donde guardaremos nuestro proyecto y ejecutamos el script que se encuentra en '/bin/roo.sh'



Si vamos tecleando 'hint' y hacemos caso de las instrucciones que nos va indicando llegaremos rápidamente a la creación de nuestro proyecto. En mi caso los comandos que he utilizado son:

project --topLevelPackage com.mlopez.appname
persistence setup --provider HIBERNATE --database POSTGRES
entity --class com.mlopez.appname.entities.Location
controller all --package ~.web

En estos momentos ya tenemos un proyecto con todo lo necesario para que se nos visualice un CRUD para una entidad de nombre 'Location'. Lo siguiente que vamos a hacer es importar este proyecto desde el eclipse y probar a ejecutarlo.

Es necesario tener instalado en eclipse el plugin m2eclipse.

Para importarlo creamos un nuevo proyecto de tipo 'Dynamic Web Project' apuntando al directorio donde ha generado todo Spring roo, Dentro del wizard tendremos que indicar que el source folder va a ser 'src/main/java', el default output folder 'target/classes' y el content directory va a ser 'src/main/webapp'

Para que coja las dependencias de Maven, sobre el proyecto hacemos botón derecho y ejecutamos "Maven -> Enable Dependency Management". De esta forma ya tendremos el proyecto sin ningún error de compilación.

Antes de arrancar la aplicación vamos a abrir el fichero 'database.properties' que se encuentra dentro del directorio 'src/main/resources/META-INF/spring' para configurar la conexión con la base de datos:

database.password=turismo
database.url=jdbc\:postgresql\://localhost\:5432
database.username=turismo
database.driverClassName=org.postgresql.Driver

Si desde el eclipse añadimos la aplicación a un tomcat y lo arrancamos veremos como se nos visualiza la siguiente pantalla:



Genial, ya tenemos nuestra aplicación funcionando con las versiones de los frameworks más nuevos sin haber gastado nada de tiempo.

Llegado a este punto, a mi ya no me interesa utilizar Spring Roo para nada y quiero empezar a introducir código desde el eclipse como yo quiera. Si echamos un vistazo a lo que ha generado Spring Roo vemos que ha generado un controller y una entidad y que su contenido está vacio y se inyecta mediante ficheros con extensión aj. Los pasos para eliminar completamente Roo de nuestra aplicación son los siguientes:

El contenido de los aj lo pasamos a su clase correspondiente y borramos posteriormente esos ficheros. Eliminamos todas las anotaciones relacionadas con Roo y en el pom.xml eliminamos la dependencia que tenemos con springroo.

Al haber añadido una anotación @Configurable en nuestra entidad es conveniente ejecutar un mvn clean de nuestro proyecto para que se vuelvan a compilar todas las clases de nuevo. Esto es necesario porque estamos utilizando 'Compile Time Weaving' para que en tiempo de compilación se modifique el class de nuestra entidad y cada vez que se cree una nueva instancia se le inyecten todos los @Autowired y @PersistenceContext que posea. Para leer más sobre esto recomiendo el siguiente weblog.

Si volvemos a arrancar la aplicación con los cambios realizados, todo debería funcionar igualmente. Ahora ya podemos trastear todo lo queramos con el código y hacer las modificaciones que creamos convenientes.