Archive for the ‘Java’ Category

Exponiendo y consumiendo servicios de datos con mybatis, play framework 2.0 y jersey

Tomado del blog gishak.wordpress.com

 

Voy a escribir en esta ocasión sobre como exponer servicios de datos con play 2.0, utilizando mybatis como mapeador de datos y la librería Jersey para consumir los servicios expuestos.

El modelo de datos con el que vamos a trabajar es el siguiente:

Como ven en el modelo no hago uso de claves foráneas, ni PK compuestas, eso es una práctica (buena o mala) personal, así que no se alarmen si no ven un modelo ER típico.

Empezando con mybatis (anteriormente ibatis); es un mapeador de datos muy simple de utilizar y muy flexible; he sido fan de hibernate desde sus primeras versiones pero me he encontrado con la simplicidad de este framework que no te quita el control del SQL que escribes (algo que para algunos desarrolladores es importante), pero a su vez hace realmente fácil el mapeo entre las tablas y los objetos.

Hay varias opciones para el mapeo de datos con mybatis, en mi caso me gusta tener la conexión a la base de datos en XML y los mapeos con anotaciones (también se pueden hacer con XML al estilo de los .hbml), así que lo primero es crear un archivo de configuración, en este caso lo llamamos dbconfig.xml.

Ahora En el archivo de configuración dbconfig.xml, como estoy utilizando jdbc de SQL Server la configuración va de la siguiente manera

<pre><configuration>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
				<property name="url" value="jdbc:sqlserver://localhost\sql2008:51085;databaseName=dummy" />
				<property name="username" value="user" />
				<property name="password" value="secret" />
			</dataSource>
		</environment>
	</environments>
</configuration>

Tomando como ejemplo del modelo una de las clases:

public class Genre implements Serializable{

	private static final long serialVersionUID = -9130550406579029310L;
	private int id;
	private String name;

	public Genre(){
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return String.format("%s - %s", this.id, this.name);
	}

}

Debemos definir una interfaz, la cual tiene las sentencias SQL y los métodos con las anotaciones correspondientes para el mapeo de los datos entre los objetos y la base de datos, los mapeos son para cualquier operación (Select, Update, Delete); para nuestro ejemplo vamos solo a hacer uso de Selects; de la clase Genre vamos a definir 2 métodos: Uno para consultar todos los registros de la base y otro para hacer una consulta por ID

public interface IGenreMapper {
	final String SELECT = "select id,name from genre";
	final String SELECT_BY_ID = "select id,name from genre where id=#{id}";

	@Select(SELECT)
	@Results(value = {
			@Result(column = "id", property = "id", javaType = Integer.class),
			@Result(column = "name", property = "name", javaType= String.class) })
	public List selectAll();

	@Select(SELECT_BY_ID)
	@Results(value = {
			@Result(column = "id", property = "id", javaType = Integer.class),
			@Result(column = "name", property = "name", javaType= String.class) })
	public Genre selectByID(@Param("id") int id);
}

Como se puede ver sobre los métodos está definida la anotación @Select que hace uso de las sentencias SQL que tenemos como Strings, y que de esas columnas seleccionadas tenemos el mapeo hacia que propiedades de la clase Genre corresponde cada una; y cuando estamos introduciendo parámetros #{nombre_párametro} en el comando sql como el caso del SELECT_BY_ID los podemos relacionar fácilmente con la anotación @Param con los párametros que se reciben en el método.

Una vez creada la interfaz hay que crear la clase que la implemente ya que ese es el objeto que finalmente se va a instanciar para hacer las consultas; la implementación es poca cosa, es simplemente invocar a una sesión de mybatis que nos devuelve un mapeador para que  internamente haga uso de lo definido en la interfaz de mapeo y obtener los resultados de la ejecución.

public class GenreDAO extends BaseDAO implements IGenreMapper{

public GenreDAO(SqlSession session){
		super(session);
	}

	@Override
	public List selectAll() {
		List result = session.getMapper(IGenreMapper.class).selectAll();
		closeSession();
		return result;
	}

	@Override
	public Genre selectByID(int id) {
		Genre result = session.getMapper(IGenreMapper.class).selectByID(id);
		closeSession();
		return result;
	}
}

Hay ciertos detalle en la clase mostrada, como por ejemplo heredar de un BaseDAO que tiene un constructor que recibe un objeto que implemente SqlSession de mybatis pero ya eso es como lo desees implementar, para mayor detalle pueden descargar el fuente relacionado al post en el link que se encuentra al final para que vean la implementación completa.

Algo que si voy a detallar es el caso cuando tenemos una propiedad compleja en nuestra clase; por ejemplo la clase Song tiene las propiedades Album y Genre que son del tipo de las clases Album y Genre respectivamente:

public class Song implements Serializable{

	private static final long serialVersionUID = -3728048783934199398L;
	private int id;
	private String name;
	private String duration;
	private Album album;
	private Genre genre;
        .
        .
        .
}

Así que para cargar las propiedades, en nuestra interface de mapeo utilizamos la anotación @One que nos permite llenar una propiedad compleja a partir de un método select que devuelva ese tipo de objeto. Como se mostro unas líneas arriba, el mapeador IGenreMapper tiene un método selectByID, del cual vamos a hacer uso en el mapeador Song para llenar la propiedad Genre, pasandole el id para que realice la consulta:

public interface ISongMapper {
	final String SELECT = "select id,album,name,duration,genre from song";

	@Select(SELECT)
	@Results( value= {
			@Result(column="id", property="id"),
			@Result(column="album", property="album", one=@One(select="com.musicservices.datamodel.mapper.IAlbumMapper.selectByID"),javaType=Album.class),
			@Result(column="name", property="name"),
			@Result(column="duration", property="duration"),
			@Result(column="genre", property="genre", one=@One(select="com.musicservices.datamodel.mapper.IGenreMapper.selectByID"), javaType=Genre.class)
	})
	public List selectAll();

}

La misma estrategia se sigue para cargar la propiedad Album, de esta manera el mapeo es completo desde nuestra base de datos relacional hacia el modelo de objetos aunque tengamos propiedades complejas.
Adicionalmente he creado una clase DataManager en la cual están las líneas donde se instancian los objetos de mybatis y el acceso a todos mis objetos DAO para centralizar el llamado a las clases, nos vamos a centrar en el código para instanciar el objeto SqlSessionFactory que es la interfaz entre nuestros objetos y la base de datos:

private DataManager(){
		if(sessionFactory == null){
			try {
				Reader reader = Resources.getResourceAsReader(DataManager.class.getClassLoader(),"dbconfig.xml");
				sessionFactory = new SqlSessionFactoryBuilder().build(reader);
				sessionFactory.getConfiguration().addMapper(IGenreMapper.class);
				sessionFactory.getConfiguration().addMapper(IAlbumMapper.class);
				sessionFactory.getConfiguration().addMapper(ISongMapper.class);
			} catch (IOException e) {
				e.printStackTrace();
			}

		}
	}

Aquí cargamos el archivo dbconfig.xml para poder crear la sesión de mybatis a través de la clase SessionFactoryBuilder, y luego de eso registramos todas las interfaces de mapeo que hayamos creado, las cuales le sirven a mybatis para hacer su trabajo, ya que como vimos en la implementación de las interfaces hacemos uso del metodo getMapper para hacer referencia a los mapeadores registrados.

Una vez con la capa de datos lista exportamos los binarios como un .jar y procedemos a exponerla como servicios Rest a traves del framework play, lo primero es descargar los binarios desde playframework.org descomprimirlo en un directorio (recomendado sin espacios en los nombres de las carpetas) y con el comando play new [NombreAplicacion] crear la estructura de un proyecto Java:

Para mayor comodidad y modificar los archivos creados por play podemos importarlo como un proyecto para Eclipse, para esto es recomendable agregar la ruta de instalación de play a las variables de ambiente para ubicarnos dentro del directorio de nuestra aplicación y ejecutar los comandos play y luego eclipsify, y luego de eso importamos el proyecto dentro de nuestro workspace de Eclipse (el paso es opcional ya que puedes editar los archivos desde cualquier editor de texto)


Navegando dentro de los directorios de nuestra aplicación play nos vamos a encontrar con las carpetas app y conf, que son las que vamos a utilizar para el ejemplo ya que solo vamos a hacer uso del framework para exponer servicios de datos, no vamos a abarcar toda la funcionalidad, solamente el enrutamiento por url hacia los controladores que van a hacer uso de nuestro modelo de datos para realizar las consultas a la base de datos.

Empezamos creando una carpeta lib en el proyecto de play y agregamos todas las dependencias de mybatis, jdbc, y el .jar creado con nuestro modelo de datos.

Ahora sí, lo primero es dentro de /app/controllers creamos un nuevo controlador Album.java que hereda de  play.mvc.controller y le agregamos un método estático public static Result all(), esta nueva notación es parte del nuevo play 2.0, en resumen vamos a crear un método que controle todos los requests realizados por HTTP GET a la ruta /album y los devuelva en formato Json, finalmente el código con todas las consideraciones antes mencionadas queda así:

public class Album extends Controller{

	@play.mvc.BodyParser.Of(Json.class)
	public static Result all(){
		ObjectMapper mapper = new ObjectMapper();
		String json = null;
		try {
			List<com.musicservices.datamodel.entities.Album> albumList = DataManager.Instance().albumDao().selectAll();
			json = mapper.writeValueAsString(albumList);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return ok(json);
	}
}

Una vez que tenemos el controlador debemos decirle a play cual es la url de la que este método del controlador se va a hacer responsable, modificamos el archivo /conf/routes y agregamos la línea para indicarle que todo lo que pase por /album y sea un HTTP GET va a ser manejado por nuestro controlador:

# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

# Home page
GET     /                           controllers.Application.index()
GET	    /album                      controllers.Album.all()

# Map static resources from the /public folder to the /assets URL path
GET     /assets/*file               controllers.Assets.at(path="/public", file)

Listo, ahora procedemos a levantar nuestra aplicación en play con el comando run

*En dias previos a la redacción de este post aparecio la versión 2.0 de play y al tratar de referenciar al archivo xml de configuración de mybatis que se encuentra dentro del jar da un error de classpath, asi que como queria publicar el post como workaround deben copiar el archivo dbConfig.xml dentro de su aplicación play en la ruta [AplicacionPlay]\target\scala-2.9.1\classes 

Una vez que tenemos todo listo accedemos por URL a localhost:9000/album y vemos el resultado es un string con notación Json de la colección de resultados obtenidos:


Bajo el mismo esquema creamos el controlador para hacer consultas de Albumes por código, donde ahora el método debe recibir un parámetro, y llamamos al metodo selectByID del mapeador:

@play.mvc.BodyParser.Of(Json.class)
	public static Result getByID(Integer id){
		ObjectMapper mapper = new ObjectMapper();
		String json = null;
		try {
			com.musicservices.datamodel.entities.Album album = DataManager.Instance().albumDao().selectByID(id.intValue());
			json = mapper.writeValueAsString(album);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return ok(json);
	}

Y en la configuración de rutas debemos indicar el mapeo hacia el nuevo metodo controlador de la siguiente manera:

# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

# Home page
GET     /                           controllers.Application.index()
GET	    /album                      controllers.Album.all()
GET	    /album/:id                 controllers.Album.getByID(id: Integer)

# Map static resources from the /public folder to the /assets URL path
GET     /assets/*file               controllers.Assets.at(path="/public", file)

Accedemos nuevamente por url a la ruta /album/{id} donde {id} en este caso es el valor de la columna id en la base de datos por la cual estamos consultando en al tabla Album, los resultados son los siguientes:

Y así podemos replicar el mismo esquema para todos los controladores que deseemos publicar. Ahora para consumir estos servicios Json vamos a hacer un simple cliente Java que haciendo uso de la libreria Jersey va a obtener via HTTP GET los resultados de la consulta y con la librería Gson de google vamos a regresarlos a su estado inicial de una Lista de objetos.

public static void main(String[] args) throws UnsupportedEncodingException{

		String albumName = URLEncoder.encode("Mane Attraction","UTF-8");
		String url = String.format("http://localhost:9000/song/album/%s",albumName);

		Client client = Client.create();
		WebResource res = client.resource(url);
		String jsonData = res.get(String.class);

		List<Song> songs = new ArrayList<>();
		Type listType = TypeToken.get(new TypeToken<Collection<Song>>(){}.getType()).getType();
		songs = new Gson().fromJson(jsonData, listType);
		for(Song s: songs){
			System.out.println(s);
		}
	}

En las primeras 2 líneas armamos la url al servicio expuesto de play, utilizando la clase URLEncoder ya que si no lo hacemos los espacios o cualquier caracter especial no se va a formatear correctamente, luego haciendo uso de las clases de Jersey generamos un WebResource de la url le hacemos un HTTP Get sólo con invocar el método del mismo nombre que nos devuelve el Json generado por nuestro servidor Rest; luego de esto tenemos que transformar este resultado en objetos manejables para java; por lo que hacemos uso de Gson, al cual tenemos que informarle el tipo de objeto al que debe transformar el String que le vamos a pasar, al pasar el tipo de la instancia de TypeToken<Collection<Song>> le indicamos que queremos transformar el Json a una lista de tipo Song, invocamos el metodo fromJson y listo estamos consumiendo ya los servicios expuestos de nuestro modelo de datos.
Los Rest services son una alternativa a los tradicionales Web Services, con play es tan simple como ya lo hemos visto; la flexibilidad de mybatis para mapear datos es fantastica y que podemos decir de las apis de Jersy y Gson que con no más de 9 líneas de código nos encapsulan una capa de comunicación y transformación de datos que debemos tener en cuenta y bajo nuestra amplia gama de frameworks para dar soluciones tecnológicas.

Saludos,

@gishac

 

Descargar fuentes

Play!, Openshift y Twitter Bootstrap: Combo para programadores perezosos pero impacientes

Ya que estan, por que no se pegan una vuelta por el sitio de openshift y votan para agregar soporte nativo para Play en Openshift.

Hace rato que descubri en Play un poderosisimo web framework para Java, y tengo la suerte de poder aplicarlo diariamente en mi actual trabajo. Sin entrar en demasiados detalles tenemos todos los beneficios del conocido patron MVC, junto con un poderoso WebApp container y un sistema de templates muy amigable. Pero lo que lo hace mas amigable y poderoso a mi parecer es la comunidad y lo facil que resulta desarrollar e integrar los famosos modulos de Play. El que mas nos ocupa hoy es el de Openshift cuyo autor es Sebastian Scarano akka @opensas (el que “mas” lo digo porque el desarrollo incluye dos mas que son nativos: CRUD y Secure)

Disclaimer: Como entiendo que mi post es acotado y especifico quizas cometa el error de dar por sentado un monton de cosas, pero si necesitan que me extienda en algun punto por favor haganmelo saber.

Instalacion y armado del entorno

Deben contar con Git y !Play instalado. Si todavia no lo hicieron, vayan a al sitio de !Play. Y tambien

(sudo) apt-get install git-core

Tambien van a necesitar el cliente de Openshift. Para ello

(sudo) gem install rhc

y

(sudo) gem install test-unit

A continuacion en su directorio de trabajo ejecutan

(sudo) play install openshift

Con esto ya tenemos el modulo de Openshift instalado en nuestro stack de !Play. A continuacion creamos nuestra aplicacion de la siguiente manera:

play new myApp --with openshift

Acto seguido haran vuestra magia con la aplicacion, corriendola en modo local y versionadolo debidamente ;-). Pero atentos que deben trabajar con Git puntualmente ya que OpenShift les creara un repo propio a donde el modulo hara el deployment.

Aca viene lo interesante: con vuestra cuenta debidamente creada en OpenShift deben agregar estas entradas en el archivo application.conf de la aplicacion en cuestion:

openshift.rhlogin = myuserOpenShift
openshift.password = myPassOplenshift
openshift.application.name = myApp@Openshift

Acto seguido vamos a instalar/desplegar por primera vez nuestra aplicacion:

play rhc:deploy -o

Cuando este comando se procese y al no existir aun la aplicacion en la nube nos preguntara si queremos crearla y si ademas queremos tener un repo en Openshift. A todo le responderemos que SI, ya que por mas que parezca en ese sentido no existe un libre albeldrio. Lo siento ;).

Hasta aqui todo muy bonito pero nuestra aplicacion es standalone y no tiene persistencia de ningun tipo. Entonces con estos comandos :

rhc-ctl-app -a myApp -e add-mysql-5.1 --rhlogin Myuser -p mypass
rhc-ctl-app -a myApp -e add-myphpadmin-3.4 --rhlogin Myuser -p mypass

y con el output de esos comandos se generan estas nuevas entradas en el application.conf

db.url=jdbc:mysql://xxx.xxx.xxx.xxxx:3306/myApp
db.driver=com.mysql.jdbc.Driver
db.user=admin
db.pass=myadminPass

Ya tenemos mysql en nuestro box al cual podemos administrar con phpmyadmin.

Pero la cereza del postre seria que pudieramos cambiar ese feo dominio myApp-myNameSpace.rhcloud.com por algo mas chevere. Para eso agregan un cname tipo host en donde tengan registrado su dominio (en es caso lo hice con GoDaddy), declarando la ip de la url anterior (con un ping ya lo tienen) y desde sus maquinas corran el siguiente comando:

rhc-ctl-app -a myApp -e add-alias mydomain.com --rhlogin Myuser -p mypass

y voila!!! Esto agrega el dominio en vuestra aplicacion. Dicho sea de paso pueden agregar mas de un dominio a una sola aplicacion.

Ok, hasta aqui llego hoy. Para no aturdilos mucho, en la proxima entrega les explico en forma muuuuy sencilla como agregue a esta misma aplicacion el Css Framework de twitter: Bootstrap.

La aplicacion la pueden ver en progreso en Blog’s @DiegoRam

Las mas grande agradecimiento para Sebastian Scarano (@opensas) y Luis Farzati (@luisfarzati) que me dieron una mano terrible en el proceso.

Saludos y comenten.

Iba a hacer una version con acentos, pero que mierda!! el codigo no lleva acentos y, como ya dije en el título de este artículo, soy muy perezoso 😉

Playframework + Google Guice

Nota: este artículo también está disponible en español.

In the project I’m currently working, we started to use Google Guice. For those who don’t know, Google Guice is a dependency injection framework. The basic idea behind dependency injection is to provide a class it’s dependencies, instead of making the dependent class responsible of instantiating the objects on which it depends.

Play has a module for integrating Guice:

http://www.playframework.org/modules/guice-1.2/home

In addition to the module documentation, this post from @_felipera can help you get started.

http://geeks.aretotally.in/dependency-injection-with-play-framework-and-google-guice

How to use the Guice module

Add the dependency

require:
  - play
  - play -> guice 1.2

Download the dependencies

play deps

Create a new class that will be injected in the controller

services.MyService

package services;
public interface MyService {
   public void sayHello();
}

services.MyServiceImpl

package services;
public class MyServiceImpl implements MyService {
    public MyServiceImpl(){
        play.Logger.info("constructor!");
    }
    
    @Override
    public void sayHello() {
        play.Logger.info("hello");
    }
}

Configure the injector

package config;
public class GuiceConfig extends GuiceSupport {
    @Override
    protected Injector configure() {
        return Guice.createInjector(new AbstractModule() {
            @Override
            protected void configure() {
                bind(MyService.class).to(MyServiceImpl.class).in(Singleton.class);
            }
        });
    }
}

This will setup the class as a singleton. Each time a class has the dependency of MyService, the injector will inject the same instance of MyServiceImpl.

Inject the dependency using the @Inject annotation

package controllers;
public class Application extends Controller {

    @Inject
    static MyService myService;

    public static void index() {
        myService.sayHello();
        render();
    }
}

Testing

My next step was to create a test and here came the first problem

play test

http://localhost:9000/@tests

Compilation error! The problem is that the module has a folder called ‘test’. This folder should have some unit or functional test, but instead it has three sample applications. The convention in play modules is to have these kind of applications in the ‘samples-and-test’ folder.
I made a fork of the project to rename this folder:

https://github.com/axelhzf/play-guice-module

I also did a pull-request but I didn’t get any response so far :

https://github.com/pk11/play-guice-module/pull/5

Renaming the folder ‘test’ was enough to run this test:

@InjectSupport
public class InjectTest extends UnitTest {
    @Inject
    static MyService myService;

    @Test
    public void injectOk(){
        assertNotNull(myService);
    }
}

Adding more dependencies

By default, Play automatically detects the @Inject annotation on classes than inherit from Controller, Job and Mail. If you want to inject dependencies on other classes you must use the @InjectSupport.

Typically our services are not as simple as MyService. It is common to have dependencies between services. Guice solves this problem analyzing the dependencies and instantiating the objects in the proper order.

services.MyDependentService

package services;

public interface MyDependentService {
    public void sayHelloWorld();
}

service.MyDependentServiceImpl

package services;

@InjectSupport
public class MyDependentServiceImpl implements MyDependentService {

    @Inject
    static MyService myService;

    public MyDependentServiceImpl(){
        play.Logger.info("Init MyDependentServiceImpl");
    }

    public void sayHelloWorld(){
        myService.sayHello();
        play.Logger.info("world");
    }
}

InjectTest

@InjectSupport
public class InjectTest extends UnitTest {

@Inject
static MyDependentService myDependentService;

@Test
public void injectOk(){
    assertNotNull(myDependentService);
    myDependentService.sayHelloWorld();
}

}

Binding in GuiceConfig

    bind(MyDependentService.class).to(MyDependentServiceImpl.class).in(Singleton.class);

And this is the console output

20:34:39,090 INFO ~ Init MyServiceImpl
20:34:39,095 INFO ~ Init MyDependentServiceImpl
20:34:39,095 INFO ~ Application 'lazySingleton' is now started !
20:34:39,136 INFO ~ hello
20:34:39,136 INFO ~ world

Constructor injection

One of the things I don’t like about the module is that the fields you are allowed to inject must be static. I would prefer to declare the dependencies as constructor parameters. This way, it would be more obvious that to create an instance of MyDependentServiceImpl you need a MyService instance. Moreover, when working on units test, it’s easier to pass a mock objects as a parameter than to configure an injector.

In the module documentation I didn’t found any reference to how to do it. Nevertheless, I found an article explaining how to do this using a Provider:

http://ericlefevre.net/wordpress/2011/05/08/play-framework-and-guice-use-providers-in-guice-modules/

Later, I found a question on StackOverflow that gave me another clue:

http://stackoverflow.com/questions/8435686/does-injector-getinstance-always-call-a-constructor

In the Edit he says that he forgot to put the @Inject annotation in the constructor. I tried to do the same thing and finally it worked:

public class MyDependentServiceImpl implements MyDependentService {

    private final MyService myService;

    @Inject
    public MyDependentServiceImpl(MyService myService){
        this.myService = myService;
        play.Logger.info("Inicializando MyDependentServiceImpl");
    }

    ...

Lazy Singletons

It still remains one last detail to have a perfect google guice configuration.

The services are initialized when the application starts.

21:38:11,801 INFO ~ Inicializando MyServiceImpl
21:38:11,805 INFO ~ Inicializando MyDependentServiceImpl
21:38:11,805 INFO ~ Application 'lazySingleton' is now started !

When the application is in production mode this is the correct behavior. But in development mode I prefer that Singletons are intialized on demand. There may be services that take their time to start and I want the application startup to be as fast as possible.

With Google Guice you can achieve this using Scopes:

http://code.google.com/p/google-guice/wiki/Scopes

All you have to do is to set the Stage parameter:

Stage stage = Play.mode.isDev() ? Stage.DEVELOPMENT : Stage.PRODUCTION;
return Guice.createInjector(stage, new AbstractModule() {…..

Rerunning the test

22:00:03,353 WARN ~ You're running Play! in DEV mode
22:00:04,615 INFO ~ Connected to jdbc:h2:mem:play;MODE=MYSQL;LOCK_MODE=0
22:00:04,811 INFO ~ Guice injector created: config.GuiceConfig
22:00:04,819 INFO ~ Init MyServiceImpl
22:00:04,824 INFO ~ Init MyDependentServiceImpl
22:00:04,824 INFO ~ Application 'lazySingleton' is now started !

Ooops! The Singleton are initialized before application starts. Perhaps that’s not the correct use for the stage variable. Let’s try a test:

public class StageTest {

    @Test
    public void testDevelopment(){
        Injector injector = createInjector(Stage.DEVELOPMENT);
        System.out.println("development - before getInstance");
        MyService instance = injector.getInstance(MyService.class);
        System.out.println("development - after getInstance");
    }

    @Test
    public void testProduction(){
        Injector injector = createInjector(Stage.PRODUCTION);
        System.out.println("production - before getInstance");
        MyService instance = injector.getInstance(MyService.class);
        System.out.println("production - after getInstance");
    }

    public Injector createInjector(Stage stage){
        Injector injector = Guice.createInjector(stage, new AbstractModule(){
            @Override
            protected void configure() {
                bind(MyService.class).to(MyServiceImpl.class);
            }
        });
        return injector;
    }
}

And the result is:

INFO: development - before getInstance
INFO: Inicializando MyServiceImpl
INFO: development - after getInstance

INFO: Inicializando MyServiceImpl
INFO: production - before getInstance
INFO: production - after getInstance

Just like it says on the documentation, in development mode Singletons are initialized lazily.

If this works, when I tried using the play module why didn’t it work?

Reviewing the code:

https://github.com/pk11/play-guice-module/blob/master/src/play/modules/guice/GuicePlugin.java

@OnApplicationStart the module finds all classes annotated with @InjectSupport and inject it’s dependencies. To inject the dependencies the module calls the getBean() method. And here we find the problem: when calling getBean() the class is instantiated.

I found a solution to this problem:

https://groups.google.com/d/msg/google-guice/405HVgnCzsQ/fBUuueP6NfsJ

This is the code:

These classes create a proxy for each class annotated as @LazySingleton. When the object is injected the injector actually injects the proxy. The first time a method is invoke, the proxy will take care of initializing the class.
Using these classes, the injector configuration looks like:

public class GuiceConfig extends GuiceSupport {
    @Override
    protected Injector configure() {
        Stage stage = Play.mode.isDev() ? Stage.DEVELOPMENT : Stage.PRODUCTION;
        return Guice.createInjector(stage, new AbstractModule() {
            @Override
            protected void configure() {
                bindScope(LazySingleton.class, MoreScopes.LAZY_SINGLETON);
                bindLazySingletonOnDev(MyService.class, MyServiceImpl.class);
                bindLazySingletonOnDev(MyDependentService.class, MyDependentServiceImpl.class);
            }

            protected &lt;T&gt; void bindLazySingletonOnDev(Class&lt;T&gt; expected, Class&lt;? extends T&gt; implClass){
                if(Play.mode.isDev()){
                    bind(implClass).in(MoreScopes.LAZY_SINGLETON);
                    Provider&lt;T&gt; provider = LazyBinder.newLazyProvider(expected, implClass);
                    bind(expected).toProvider(provider);
                }else{
                    bind(expected).to(implClass).in(Scopes.SINGLETON);
                }
            }
        });
    }
}

I will add this classes to the fork in order to have a complete module that can be reused across projects.

Conclusion

In the last few years Dependency Injection has has evolved from being an obscure and little understood term, to become part of every programmer’s toolchest. In this article we saw how easy is to integrate Guice, a very handy library from Google, into a play framework application. Moreover, we also covered how to customize it’s behaviour for a better development experience.

Article original published at http://axelhzf.tumblr.com.

Playframework + Google Guice (Español)

Note: This article is also available in english.

En el proyecto en el que estoy trabajando actualmente empezamos a utilizar Google Guice. Para quien no lo sepa, Guice es un framework de inyección de dependencias. La idea básica de la inyección de dependencias consiste en sumistrar a una clase sus dependencias, en lugar de que sea esta quien tenga que instanciarlas.

Play cuenta con un módulo para integrar Guice:

http://www.playframework.org/modules/guice-1.2/home

Además de la propia documentación del módulo, está este post de @_felipera que te puede ayudar a dar los primeros pasos:

http://geeks.aretotally.in/dependency-injection-with-play-framework-and-google-guice

Cómo usar el módulo

Añadir la dependencia:

require:
  - play
  - play -> guice 1.2

Descargar las dependencias

play deps

Crear una nueva clase que será la que se inyectará en el controlador.

services.MyService

package services;
public interface MyService {
   public void sayHello();
}

services.MyServiceImpl

package services;
public class MyServiceImpl implements MyService {
    public MyServiceImpl(){
        play.Logger.info("constructor!");
    }

    @Override
    public void sayHello() {
        play.Logger.info("hello");
    }
}

Configurar el inyector de dependencias

package config;
public class GuiceConfig extends GuiceSupport {
    @Override
    protected Injector configure() {
        return Guice.createInjector(new AbstractModule() {
            @Override
            protected void configure() {
                bind(MyService.class).to(MyServiceImpl.class).in(Singleton.class);
            }
        });
    }
}

De esta forma se configura la clase como un singleton. Cada vez que una clase tenga la dependencia de MyService se inyectará la misma instancia de MyServiceImpl.

Para inyectar la clase se utiliza la anotación @Inject

package controllers;
public class Application extends Controller {

    @Inject
    static MyService myService;

    public static void index() {
        myService.sayHello();
        render();
    }
}

Testing

Mi siguiente paso fue crear un test y es aquí cuando me encontré una sorpresa:

play test

http://localhost:9000/@tests

Compilation error! El problema está en que el módulo tiene una carpeta que se llama test. Esta carpeta en vez de tener algunos tests unitarios o funcionales, lo que tiene son 3 aplicaciones de ejemplo. Lo normal hubiera sido seguir la convención de play que es poner este tipo de aplicaciones en la carpeta ‘samples-and-tests’.

Hice un fork del proyeto para renombrar esta carpeta:

https://github.com/axelhzf/play-guice-module

También hice un pull-request, pero no he tenido respuesta:

https://github.com/pk11/play-guice-module/pull/5

Renombrando la carpeta test del módulo sería suficiente para poder ejecutar este test:

@InjectSupport
public class InjectTest extends UnitTest {
    @Inject
    static MyService myService;

    @Test
    public void injectOk(){
        assertNotNull(myService);
    }
}

Añadiendo más dependencias

Por defecto play detecta automáticamente la anotaciones @Inject en las clases que hereden de Controller, Job and Mail. Si queremos poder inyectar dependencias en otras clases debemos anotar la clase con @InjectSupport.

Normalmente nuestros servicios no son tan simples como MyService. Lo normal es tener dependencias entre servicios. Guice resuelve esto analizando las dependencias e instanciando los objetos en el orden adecuado.

services.MyDependentService

package services;

public interface MyDependentService {
    public void sayHelloWorld();
}

service.MyDependentServiceImpl

package services;

@InjectSupport
public class MyDependentServiceImpl implements MyDependentService {

    @Inject
    static MyService myService;

    public MyDependentServiceImpl(){
        play.Logger.info("Init MyDependentServiceImpl");
    }

    public void sayHelloWorld(){
        myService.sayHello();
        play.Logger.info("world");
    }
}

InjectTest

@InjectSupport
public class InjectTest extends UnitTest {

@Inject
static MyDependentService myDependentService;

@Test
public void injectOk(){
    assertNotNull(myDependentService);
    myDependentService.sayHelloWorld();
}

}

Binding en GuiceConfig

    bind(MyDependentService.class).to(MyDependentServiceImpl.class).in(Singleton.class);

Y esta es la salida por la consola

20:34:39,090 INFO ~ Init MyServiceImpl
20:34:39,095 INFO ~ Init MyDependentServiceImpl
20:34:39,095 INFO ~ Application 'lazySingleton' is now started !
20:34:39,136 INFO ~ hello
20:34:39,136 INFO ~ world

Inyección por parámetro del constructor

Una de las cosas que no me gusta del módulo es que te limita a que los campos que puedes inyectar deben de ser estáticos. Las dependencias por ejemplo me gustaría poder definirlas como parámetros en el constructor. De forma que quede claro que para crear un objeto de la clase MyDependentServiceImpl hace falta un objeto del tipo MyService. Además, utilizar las dependencias mediante constructor facilita hacer tests unitarios.Únicamente es necesario llamar al constructor y pasar como parámetros stubs o mocks de las dependencias. De esta forma no estamos obligados a configurar un inyector.

En la documentación del módulo no vi ninguna referencia a cómo hacer esto. Encontré un artículo que explicaba cómo hacerlo utilizando un Provider:

http://ericlefevre.net/wordpress/2011/05/08/play-framework-and-guice-use-providers-in-guice-modules/

Esta forma funciona correctamente pero más tarde encontré una pregunta en stackoverflow que me dio otra pista:

http://stackoverflow.com/questions/8435686/does-injector-getinstance-always-call-a-constructor

En el Edit pone que se olvidó de anotar con @Inject el constructor. Probé a hacer lo mismo y funcionó:

public class MyDependentServiceImpl implements MyDependentService {

    private final MyService myService;

    @Inject
    public MyDependentServiceImpl(MyService myService){
        this.myService = myService;
        play.Logger.info("Inicializando MyDependentServiceImpl");
    }

    ...

Lazy Singletons

Me faltaba un pequeño detalle para tener google guice configurado perfectamente. En el log se puede ver como los servicios se inicializan cuando se inicia la aplicación.

21:38:11,801 INFO ~ Inicializando MyServiceImpl
21:38:11,805 INFO ~ Inicializando MyDependentServiceImpl
21:38:11,805 INFO ~ Application 'lazySingleton' is now started !

Cuando la aplicación está en modo producción está bien, es el comportamiento adecuado. Los servicios se deberían instanciar al arrancar la aplicación. Pero cuando estoy en modo desarrollo prefiero que los Singletons se inicialicen bajo demanda (lazy). Puede que haya servicios que tarden en iniciarse y quiero que el tiempo que tarda la aplicación en arrancar en modo desarrollo sea lo más rápido posible.

Buscando en la documentación de google guice veo que está preparado para hacer justamente lo que quiero:

http://code.google.com/p/google-guice/wiki/Scopes

Lo único que hay que hacer es que configurar es el STAGE para indicarle a Guice si estamos en modo desarrollo o en modo producción:

Stage stage = Play.mode.isDev() ? Stage.DEVELOPMENT : Stage.PRODUCTION;
return Guice.createInjector(stage, new AbstractModule() {…..

Al volver a ejecutar el test

22:00:03,353 WARN ~ You're running Play! in DEV mode
22:00:04,615 INFO ~ Connected to jdbc:h2:mem:play;MODE=MYSQL;LOCK_MODE=0
22:00:04,811 INFO ~ Guice injector created: config.GuiceConfig
22:00:04,819 INFO ~ Init MyServiceImpl
22:00:04,824 INFO ~ Init MyDependentServiceImpl
22:00:04,824 INFO ~ Application 'lazySingleton' is now started !

Oops! se volvieron a instanciar los singletons al iniciar la aplicación. ¿Será que el Stage no sirve para lo que creo? Vamos a probar con un test:

public class StageTest {

    @Test
    public void testDevelopment(){
        Injector injector = createInjector(Stage.DEVELOPMENT);
        System.out.println("development - before getInstance");
        MyService instance = injector.getInstance(MyService.class);
        System.out.println("development - after getInstance");
    }

    @Test
    public void testProduction(){
        Injector injector = createInjector(Stage.PRODUCTION);
        System.out.println("production - before getInstance");
        MyService instance = injector.getInstance(MyService.class);
        System.out.println("production - after getInstance");
    }

    public Injector createInjector(Stage stage){
        Injector injector = Guice.createInjector(stage, new AbstractModule(){
            @Override
            protected void configure() {
                bind(MyService.class).to(MyServiceImpl.class);
            }
        });
        return injector;
    }
}

Y el resultado es:

INFO: development - before getInstance
INFO: Inicializando MyServiceImpl
INFO: development - after getInstance

INFO: Inicializando MyServiceImpl
INFO: production - before getInstance
INFO: production - after getInstance

Como pone en la documentación, cuando se está en modo DEVELOPMENT los Singleton se inicializan de forma lazy.

¿Si esto funciona así por qué cuando lo probé con el módulo de play no funcionó?

Revisando el código:

https://github.com/pk11/play-guice-module/blob/master/src/play/modules/guice/GuicePlugin.java

Encontré que lo que se hace en el @OnApplicationStart es buscar todas las clases que están anotadas con @InjectSupport las dependencias. Para inyectarlas hace un getBean de cada una. Aquí esta el problema, al hacer el getBean se instancia.

Buscando en internet encontré una solución a este problema:

https://groups.google.com/d/msg/google-guice/405HVgnCzsQ/fBUuueP6NfsJ

Este es el código:

Estas clases lo que hacen es que cuando se crea el inyector, crea un proxy para cada una de las clases que están anotadas como @LazySingleton. De forma que cuando inyecta los objetos lo que se inyecta en realidad es el proxy. La primera vez que se invoque un método de alguna de estas clases, el próxy se va a encargar de inicializar la clase.

La configuración del inyector quedaría así:

public class GuiceConfig extends GuiceSupport {
    @Override
    protected Injector configure() {
        Stage stage = Play.mode.isDev() ? Stage.DEVELOPMENT : Stage.PRODUCTION;
        return Guice.createInjector(stage, new AbstractModule() {
            @Override
            protected void configure() {
                bindScope(LazySingleton.class, MoreScopes.LAZY_SINGLETON);
                bindLazySingletonOnDev(MyService.class, MyServiceImpl.class);
                bindLazySingletonOnDev(MyDependentService.class, MyDependentServiceImpl.class);
            }

            protected <T> void bindLazySingletonOnDev(Class<T> expected, Class<? extends T> implClass){
                if(Play.mode.isDev()){
                    bind(implClass).in(MoreScopes.LAZY_SINGLETON);
                    Provider<T> provider = LazyBinder.newLazyProvider(expected, implClass);
                    bind(expected).toProvider(provider);
                }else{
                    bind(expected).to(implClass).in(Scopes.SINGLETON);
                }
            }
        });
    }
}

Cuando la aplicación está en modo desarrollo, las clases se instanciarán la primera vez que se llame a un método. Cuando usemos el modo producción, las clases se instanciarán cuando se inicie la aplicación.

Me queda pendiente añadir estas clases al fork para poder tener un módulo completo que se pueda reutilizar en todos los proyectos.

Conclusión

Durante los últimos años la inyección de dependencia ha evolucionado convirtiéndose en un recurso que utilizan a diario los programadores. En este artículos hemos visto lo sencillo que es integrar Guice en una aplicación que utiliza play framework. También hemos visto cómo configurar la librería para conseguir una mejor experiencia durante el desarrollo.

Artículo original publicado en http://axelhzf.tumblr.com.

Divide and conquer: Play framework modules

It’s usually the case that you start developing an application and go on fulfilling requirements. When your application grows bigger you start to realize the convenience of separating it into different components. Moreover, when you develop your second or third application, your begin to recognize certain features that could be reused across different applications.

These are two good reasons to modularize an application. Ideally we should aim at components with high cohesion and low coupling.

The Java language has proven itself quite fit to accomplish this kind of tasks. It provides general means to enforce the use of well defined API thru interfaces, abstract classes, etc.

Play framework developers think that this is perfectly fine for developing a general purpose library, but in the case of a web application reusability and modularization could be best achieved by other means. Have a look at this excerpt taken from play framework’s FAQ:

Java itself is a very generic programming language and not originally designed for web application development. It is a very different thing to write a generic and reusable Java library and to create a web application. A web application itself doesn’t need to be designed to be reusable. You need less abstraction, less configuration. Reusability does exist for web applications, but through web service APIs rather than language-level integration.

So when it comes to reusability, play provides us with a solution better suited for web applications.

Play modules

A module is just another Play framework application. The only difference is that a module is not meant to be run on his own, it needs to be included into a containing application.

Nevertheless, there area a couple of differences between a module and a regular application, mainly that a module has no conf file (it has to be provided by the main application) and everything in a module is optional.

Doing is better than saying, so as usual we will look for a fine opportunity to make a simple module to show how it works.

Creating a new play framework application and deploying it to the cloud

As many of you already know, we are working on the spanish translation of play framework site. We wanted to add web analytics to it so that we can see how people were using it.

So in order to follow this example, we’ll need a play framework app deployed somewhere on the Internet. Nowadays there are lots of Java hosting options available for free. Here you have a couple of tutorials for deploying on openshift, google application engine and heroku.

First let’s create a play framework application, I’ll create the app at ~/devel/apps/module-test, you can choose whatever location you like, just be sure to update the commands appropriately.

To create the app, run the following command at an os prompt:

sas@ubuntu:~/devel/apps/module-test$ play new analytics-app
~        _            _
~  _ __ | | __ _ _  _| |
~ | '_ \| |/ _' | || |_|
~ |  __/|_|\____|\__ (_)
~ |_|            |__/
~
~ play! 1.2.4, http://www.playframework.org
~
~ The new application will be created in /home/sas/Dropbox/Public/devel/play/apps/module-test/analytics-app
~ What is the application name? [analytics-app]
~
~ OK, the application is created.
~ Start it with : play run analytics-app
~ Have fun!

Now it would be a good time to deploy it somewhere. For this tutorial we will deploy it at openshift, you can use any host you want (For more information on setting up your environment for openshift deployment follow this tutorial)

Consider voting for Play framework native support on Openshift.

Create a new directory at ~/devel/apps/module-test/openshift, go to that directory and run:

rhc-create-app -l mymail@mail.com -p mypassword -t jbossas-7.0 -a analyticsapp

Attempting to create remote application space: analyticsapp
Now your new domain name is being propagated worldwide (this might take a minute)...
Pulling new repo down

[...]

Successfully created application: analyticsapp

Next, we’ll get rid of the demo app:

cd ~/devel/apps/module-test/openshift/analyticsapp
rm -fr pom.xml src

And we’ll compile and package the newly created app as an exploded war. Go to ~/devel/apps/module-test folder and run:

cd ~/devel/apps/module-test
play war analytics-app -o openshift/analyticsapp/deployments/ROOT.war
~        _            _
~  _ __ | | __ _ _  _| |
~ | '_ \| |/ _' | || |_|
~ |  __/|_|\____|\__ (_)
~ |_|            |__/
~
~ play! 1.2.4, http://www.playframework.org
~
JPDA port 8000 is already used. Will try to use any free port for debugging
Listening for transport dt_socket at address: 53978
00:22:38,021 INFO  ~ Starting /home/sas/Dropbox/Public/devel/play/apps/module-test/analytics-app
00:22:39,891 INFO  ~ Precompiling ...
00:22:49,075 INFO  ~ Done.
~ Packaging current version of the framework and the application to /home/sas/Dropbox/Public/devel/play/apps/module-test/openshift/analyticsapp/deployments/ROOT.war ...
~ Done !
~
~ You can now load /home/sas/Dropbox/Public/devel/play/apps/module-test/openshift/analyticsapp/deployments/ROOT.war as a standard WAR into your servlet container
~ You can't use play standard commands to run/stop/debug the WAR application...
~ ... just use your servlet container commands instead
~
~ Have fun!
~

Now we just need to commit the application and push it to our git repo on openshift:

cd ~/devel/apps/module-test/openshift/analyticsapp
touch deployments/ROOT.war.dodeploy
git add -A
git commit -m "deploy play framework app"
git push origin

Note: The first time it will take a couple of minutes to push the application, because of play framework libraries. Later pushes will be much quicker, git is smart enough to only send updated files.

And that’s it, you’ve just deployed your first app to red hat’s cloud. You can see it running at http://analyticsapp-opensas.rhcloud.com/ (of course, you’ll have to replace “opensas” with your own openshift user name).

Google web analytics & play framework

Adding Google web analytics to a play app is really easy. You just need a gmail account, then go to Google Analytics web site, click on “sign up”, login with your gmail account, and complete all the required data.

In the account name enter “analytics-app”, in the website’s url enter http://analyticsapp-opensas.rhcloud.com/, agree to the terms and conditions and click on “Create account”.

You’ll be taken to your analytics-app account page, there you can see the tracking code. You’ll just have to paste it in your app. So open the file at ~/devel/apps/module-test/analytics-app/app/views/main.html and paste the tracking code before the closing head tag, like this:

[...]
<script charset="${_response_encoding}" type="text/javascript" src="@{'/public/javascripts/jquery-1.6.4.min.js'}"></script>#{get 'moreScripts' /}<script type="text/javascript">
  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-XXXXXXXX-1']);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  })();
</script>
[...]

Note: Google will provide you with your own UA-XXXXXXXX-1 account code, so just copy and paste the code from your Google analytics account page, and NOT from this page!

Now you just have to generate the war folder, commit, and push it once again to openshift to deploy your changes. Every time you make a change you’ll have to follow these same steps to deploy it to openshift.

cd ~/devel/apps/module-test
play war analytics-app/ -o openshift/analyticsapp/deployments/ROOT.war
cd openshift/analyticsapp/
git add -A
git commit -m "added tracking code"
git push origin

Visit again your page at http://analyticsapp-opensas.rhcloud.com/, and see the source of the page to check that the tracking code has been added. You can also see it in action on Google’s analytics page, click on “Home”, Real-Time (BETA), and the Overview. You should have one visitor (yes, it’s you!).

So far now we created a new play application and deployed it to openshift. Then we created a Google analytic account and added the tracking code the our play application. Everything is working ok and our app is being tracked by Google. Now we are going to move that functionality to a module so that we can reuse it from other apps.

Creating a module

To create a new module you have to use the “new-module” play command, like this:

cd /home/sas/devel/apps/module-test/
play new-module analytics

Now, in order to tell our main application (in our case analytics-app) to include this module, we have to configure a local repository.

Edit ~/devel/apps/module-test/analytics-app/conf/dependencies.yml like this:

# Application dependencies

require:
    - play
    - analytics -> analytics

repositories:
    - My local modules:
        type:       local
        artifact:   ${application.path}/../[module]
        contains:
            - analytics

and after that run the following command to tell play to resolve dependencies.

cd ~/devel/apps/module-test/analytics-app
play dependencies
~        _            _
~  _ __ | | __ _ _  _| |
~ | '_ \| |/ _' | || |_|
~ |  __/|_|\____|\__ (_)
~ |_|            |__/
~
~ play! 1.2.4, http://www.playframework.org
~
~ Resolving dependencies using /home/sas/devel/apps/module-test/analytics-app/conf/dependencies.yml,
~
~ 	analytics->analytics -> (from My local modules)
~
~ Installing resolved dependencies,
~
~ 	modules/analytics -> /home/sas/devel/apps/module-test/analytics/../analytics
~
~ Done!
~

You can now start the main application on your workstation:

cd ~/devel/apps/module-test/analytics-app
play run

You can see your app running at http://localhost:9000.

Moving the tracking code to a reusable tag

Now we will move the tracking code to a tag defined in the module, so we’ll create a file ~/devel/apps/module-test/analytics/app/views/analytics.html with the tracking code, like this:

<script type="text/javascript">
  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-XXXXXXXX-1']);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  })();
</script>

And now, replace the tracking code in main.html with a call to the tag, like this:

[...]
<script charset="${_response_encoding}" type="text/javascript" src="@{'/public/javascripts/jquery-1.6.4.min.js'}"></script>
        #{get 'moreScripts' /}

#{analytics /}

[...]

 

Getting module configuration from the application.conf file

Our module is almost ready, there’s just one thing that’s preventing us from really reusing it on another application: the Google analytics code is hardcoded in our tag! So we will read it from the application.conf file. Just edit the analytics.html tag like this:

%{
    String code = play.Play.configuration.getProperty("analytics.code", "")
}%
#{if code!=""}<script type="text/javascript">
  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', '${code}}']);
  _gaq.push(['_trackPageview']);
  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  })();
</script>
#{/if}

and add the following to your main application configuration file at ~/devel/apps/module-test/analytics-app/conf/application.conf

analytics.code=UA-XXXXXXXX-1

Prevent tracking in dev mode

This tag will will update the tracker every time the page is rendered, even when we are working on our development workstation!

So we will add a minor improvement to prevent the module from logging the page activity while working in development mode.

Just add the following condition to the code:

%{
    String code = play.Play.configuration.getProperty("analytics.code", "")
}%
#{if play.mode.isProd() && code!=""}
<script type="text/javascript">

  var _gaq = _gaq || [];
[...]

Troubleshooting Openshift

Openshift won’t be able to resolve the relative reference to the module location (and in fact any war deployed application will have the same problem), so you’ll have to tell play to copy the module sources to the containing application before generating the war folder. Just issue:

cd ~/devel/apps/module-test/analytics-app
play dependencies --forceCopy

And that’s it, now you can deploy to openshift in the usual way:

cd ~/devel/apps/module-test
play war analytics-app/ -o openshift/analyticsapp/deployments/ROOT.war
cd openshift/analyticsapp/
git add -A
git commit -m "added analytics module"
git push origin

Run your site locally with “play run”, and also open it from http://analyticsapp-opensas.rhcloud.com/ (go on, give it a try, every click counts), check the source code of both sites, you should see that the app running at openshift contains the tracking code, contrary to your local application.

Conclusion

In this post we saw how to deploy a play framework app to openshift and, more important, how to move features from your application to a module in order to reuse it from other applications.

In the meantime, I’ve also added google analytics support to Play! framework spanish translation site. Here you can see it in action:

Google analytics realtime in action
Tracking visits from Colombia, Argentina, Perú, El Salvador
Venezuela, Costa Rica, Ecuador, México, Spain… in realtime

You can learn more about modules on this article or reading play’s documentation.

If you speak spanish you can help us with the translation, and you can also have a look at our work right here… You can be sure every click you make will be tracked!

%d bloggers like this: