Play!ing (2.0) with Twitter Bootstrap, WebSockets, Akka and OpenLayers

The original post can be found on the ekito website.

For one of our client, we need to show a map with vehicles position updated in real-time.
So I began to make a prototype using Play! framework, with its latest released version 2.0, using the Java API. I started from the websocket-chat of the Play! 2.0 samples.

The purpose of the prototype is to show a moving vehicle on a map. The location of the vehicle is sent to the server through a REST call (at the end, it will be sent by an Android app), and the connected users can see the vehicle moving in real-time on their map.

First, let’s look at a small demo !

So, at first, to make the things a bit pretty, I decided to integrate Twitter Bootstrap (v2.0.1) using LessCss. For that, I used the tips from the following article (nothing difficult here).

Then, I integrated OpenLayers, a Javascript framework used for map visualization. I used the Google Maps integration example, and add some KML layers. This is done in the map.scala.html and maptracker.js files, nothing fancy here (it is pure Javascript, and I’m not an expert…).

The interesting part is the one using the WebSockets. On the client side, it is quite standard :

var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
var mapSocket = new WS("@routes.Application.mapsocket().webSocketURL(request)");

mapSocket.onmessage = function(event) {
    var data = JSON.parse(event.data);
        
    marker = moveMaker(map, marker, data.longitude, data.latitude);
        
}

// if errors on websocket
var onalert = function(event) {
    $(".alert").removeClass("hide");
} 

mapSocket.onerror = onalert;
mapSocket.onclose = onalert;

When the client receive a JSON data from the websocket, it moves the marker on the map. And if an error occurs on the websocket (the server is stopped for instance), a pretty error is displayed thanks to Twitter Bootstrap :

On the server part, the websocket is created by the Application controller, and is handled by the MapAnime.java Akka actor; it accesses Akka native libraries to deal with the events from the controller.

public class MapAnime extends UntypedActor {

	static ActorRef actor = Akka.system().actorOf(new Props(MapAnime.class));

	Map<String, WebSocket.Out<JsonNode>> registrered = new HashMap<String, WebSocket.Out<JsonNode>>();

	/**
	 * 
	 * @param id
	 * @param in
	 * @param out
	 * @throws Exception
	 */
	public static void register(final String id,
			final WebSocket.In<JsonNode> in, final WebSocket.Out<JsonNode> out)
			throws Exception {

		actor.tell(new RegistrationMessage(id, out));

		// For each event received on the socket,
		in.onMessage(new Callback<JsonNode>() {
			@Override
			public void invoke(JsonNode event) {
				// nothing to do
			}
		});

		// When the socket is closed.
		in.onClose(new Callback0() {
			@Override
			public void invoke() {
				actor.tell(new UnregistrationMessage(id));
			}
		});
	}

	public static void moveTo(float longitude, float latitude) {

		actor.tell(new MoveMessage(longitude, latitude));

	}

	@Override
	public void onReceive(Object message) throws Exception {

		if (message instanceof RegistrationMessage) {

			// Received a Join message
			RegistrationMessage registration = (RegistrationMessage) message;

			Logger.info("Registering " + registration.id + "...");
			registrered.put(registration.id, registration.channel);

		} else if (message instanceof MoveMessage) {

			// Received a Move message
			MoveMessage move = (MoveMessage) message;

			for (WebSocket.Out<JsonNode> channel : registrered.values()) {

				ObjectNode event = Json.newObject();
				event.put("longitude", move.longitude);
				event.put("latitude", move.latitude);

				channel.write(event);
			}

		} else if (message instanceof UnregistrationMessage) {

			// Received a Unregistration message
			UnregistrationMessage quit = (UnregistrationMessage) message;

			Logger.info("Unregistering " + quit.id + "...");
			registrered.remove(quit.id);

		} else {
			unhandled(message);
		}

	}

	public static class RegistrationMessage {
		public String id;
		public WebSocket.Out<JsonNode> channel;

		public RegistrationMessage(String id, WebSocket.Out<JsonNode> channel) {
			super();
			this.id = id;
			this.channel = channel;
		}
	}

	public static class UnregistrationMessage {
		public String id;

		public UnregistrationMessage(String id) {
			super();
			this.id = id;
		}
	}

	public static class MoveMessage {

		public float longitude;

		public float latitude;

		public MoveMessage(float longitude, float latitude) {
			this.longitude = longitude;
			this.latitude = latitude;
		}

	}

}

The “register” and “moveTo” methods are called by the controller, they send messages to the Akka system. These messages are processed by the “onReceive” method. For instance, when it receives a MoveMessage, it creates a JSON object with the longitude and the latitude and it is sent to the clients through the websockets.

I also quickly wrote a test class which parse a text file, and send REST requests with a new location to the server every 100ms.

The project is hosted on Github. It works with Google Chrome v17 and Firefox v11.

To test it,

The problem I need to solve now is that the application is not Stateless, because in the Actor, I store a Map of connected clients. Maybe I’ll need to look at Redis or something, any help would be greatly appreciated.

So in conclusion, I was able to quickly develop a working prototype, and I think I’ll try to use Play! 2.0 in several projects ;-)

What’s good:

  • Highly productive
  • Typesafe view templates based on Scala
  • LessCss integration
  • Akka integration
  • Compiled javascript with Google Closure Compiler
  • No need to learn Scala for the moment, hooray ! ;-)

To be improved:

  • The Scala compile times should be increased, because on my PC, it takes up to 4s to compile a view, and it breaks my flow (I use the “~run” command to gain 1s when switching from my IDE to my web browser)
  • The Scala compiler errors are cryptic
  • I cannot deploy the demo on Heroku because it does not support (yet ?) websockets

Update : A bit later, I discovered an article from @steve_objectify using similar technologies: http://www.objectify.be/wordpress/?p=341

Play Framework 2.0 quicktip: ¿cómo obtener un valor POST desde un controller?

Suena tonto, pero me llevó varios minutos y no lo encontré en la documentación online. Creo que es algo bastante común.

Quiero acceder a un valor POST simple desde un controller en Play Framework 2.0…

En este caso el atributo “filter”:

String filter = request().body().asFormUrlEncoded().get(“filter”)[0];

Eso es todo.

Tip Play Framework 2.0: how to get a single POST value?

Sounds silly, but I spent some minutes on it and couldn’t find anything in the online documentation. I think it’s quite common.

I want to get a single POST value from controller in Play Framework 2.0

In this case the attribute is called “filter”:

String filter = request (). Body (). AsFormUrlEncoded (). Get (“filter”) [0];

That’s it.

———————————————————–

Play Framework Main Site: http://www.playframework.org/

Play Framework Latam Blog: http://playlatam.wordpress.com/

Play Framework Google Groups:

Source: http://blog.palamago.com.ar/2012/04/tip-play-framework-2-0-como-obtener-un-valor-post-desde-un-controller/

Play framework 2 quicktip: interactively play with your application from the scala console

Short version for impatients:

path_to_your_play2_app$ play console
scala> new play.core.StaticApplication(new java.io.File("."))

And don’t forget that the tab key is your friend!


When I first started to play with Scala, I was amazed by the Scala interactive interpreter (also known as REPL, read-evaluate-print-loop). It was one of those things that you never expected to find in a statically typed, compiled language like java or scala.

What would you say if we could have it for our play applications?… In scala OR JAVA! And yes, with tab completion and all the bells-and-whistles…

Well, thanks to Peter Hausel (@pk11) from the play dev team, I found out how to do it.

Just open a command prompt and type:

cd <path_to_your_play2_app>
play console

[info] Loading project definition from /home/sas/Dropbox/Public/devel/play/apps/play2/todo/project
[info] Set current project to todo (in build file:/home/sas/Dropbox/Public/devel/play/apps/play2/todo/)
[info] Starting scala interpreter...
[info] 
Welcome to Scala version 2.9.1.final (Java HotSpot(TM) Client VM, Java 1.6.0_31).
Type in expressions to have them evaluated.
Type :help for more information.

That’s it, you are at the scala console, and what’s best, with tab completion enabled!

Right there you can start playing with scala, and also with your application, like this:

scala> val contact = models.Contact(1, "new contact", "new address")
contact: models.Contact = Contact(1,new contact,new address)

You can test your views, and of course you can also issue imports to save yourself quite a few keystrokes

scala> import models._, views.html._
import models._
import views.html._

scala> val contacts = Seq( Contact(1, "@develsas", "Buenos Aires"), Contact(2, "@pk11", "Paris") )
contacts: Seq[models.Contact] = List(Contact(1,@develsas,Buenos Aires), Contact(2,@pk11,Paris))

scala> contact.list(contacts)
res1: play.api.templates.Html = 
<!DOCTYPE html>
<html>
    <head>
        <title>Contact list</title>
        <link rel="styles
[...]
<tr>
	<td>@develsas</td>
	<td>Buenos Aires</td>
</tr>
<tr>
	<td>@pk11</td>
	<td>Paris</td>
</tr>
[...]

But when you try to access your database you’ll get the following error:

scala> val contacts = Contact.all()
java.lang.RuntimeException: There is no started application
	at scala.sys.package$.error(package.scala:27)
[...]

That’s easy, you just have to start your application

scala> import play.core._
scala> new StaticApplication(new java.io.File("."))
[info] play - database [default] connected at jdbc:h2:data/db
[info] play - Application started (Prod)
res1: play.core.StaticApplication = play.core.StaticApplication@10cdd4

And now you can play arround interactively with your running application

scala> val contacts = Contact.all
contacts: Seq[models.Contact] = List(Contact(1,Paul,Boston), Contact(3,Paolo,Roma), Contact(4,Paulain,Paris), Contact(5,Abelardo,San Justo))

scala> val newContact = Contact(6, "new contact", "new address")
newContact: models.Contact = Contact(6,new contact,new address)

scala> Contact.insert(newContact)
res2: Int = 1

scala> val contacts = Contact.all
contacts: Seq[models.Contact] = List(Contact(1,Paul,Boston), Contact(3,Paolo,Roma), Contact(4,Paulain,Paris), Contact(5,Abelardo,San Justo), Contact(6,new contact,new address))

Now go check your database, you’ll see the new contact has been persisted to your db.

One last tip. If you are working against the in-memory database, and you have defined any evolution script, you’ll receive this message when you start the application form play console:

scala> new play.core.StaticApplication(new java.io.File("."))
[info] play - database [default] connected at jdbc:h2:mem:play
[warn] play - Your production database [default] needs evolutions! 

# --- Rev:1,Ups - 74ff2d1
[...]

[warn] play - Run with -DapplyEvolutions.default=true if you want to run them automatically (be careful)
PlayException: Database 'default' needs evolution! [An SQL script need to be run on your database.]
	at play.api.db.evolutions.EvolutionsPlugin$$anonfun$onStart$1.apply(Evolutions.scala:422)
[...]

The solution is quite easy, just start play with

play -DapplyEvolutions.default=true

And your evolution scripts will be automatically applied when you start the application.

Go ahead and give it a try. Grab, for example, the computer-database java demo

$ cd <path_to_your_play2_installation>/samples/java/computer-database
$ play -DapplyEvolutions.default=true

[computer-database] $ console
[info] Starting scala interpreter...
[info] 
Welcome to Scala version 2.9.1.final (Java HotSpot(TM) Client VM, Java 1.7.0_03).
Type in expressions to have them evaluated.
Type :help for more information.


scala> new play.core.StaticApplication(new java.io.File("."))
[info] play - database [default] connected at jdbc:h2:mem:play
[info] play - Application started (Prod)
res0: play.core.StaticApplication = play.core.StaticApplication@129796b

And now, just start playing around with your app

scala> val page = models.Computer.page(1, 10, "name", "asc", "")
page: com.avaje.ebean.Page[models.Computer] = com.avaje.ebeaninternal.server.query.LimitOffsetPage@d386c9

scala> val computerList = page.getList()
computerList: java.util.List[models.Computer] = BeanList size[10] hasMoreRows[true] list[models.Computer@137, models.Computer@20d, models.Computer@12e, models.Computer@1b7, models.Computer@12d, models.Computer@14a, models.Computer@14b, models.Computer@21f, models.Computer@98, models.Computer@1a2]

scala> computerList.get(0).name
res2: java.lang.String = ASCI White

Nevertheless, as soon as you start to play with the db, I faced a couple of problems. I could read from the database but I coudn’t write update nor inserts. I guess it’s the ebean voodoo magic that’s giving me troubles.

With the scala version, I could really go much further, look at this (go ahead, don’t be shy and copy-paste this code)

$ cd <path_to_your_play2_installation>/samples/scala/computer-database
$ play -DapplyEvolutions.default=true

[computer-database] $ console
[info] Starting scala interpreter...
[info] 
Welcome to Scala version 2.9.1.final (Java HotSpot(TM) Client VM, Java 1.7.0_03).
Type in expressions to have them evaluated.
Type :help for more information.

scala> new play.core.StaticApplication(new java.io.File("."))
[info] play - database [default] connected at jdbc:h2:mem:play
[info] play - Application started (Prod)
res0: play.core.StaticApplication = play.core.StaticApplication@1eaf4b3

scala> import models._, anorm._, play.api.db._, play.api.Play.current, anorm.SqlParser._

import models._
import anorm._
import play.api.db._
import play.api.Play.current
import anorm.SqlParser._

scala> // clean-up everything

scala> DB.withConnection { implicit connection => 
     SQL("delete from computer").executeUpdate()
     SQL("delete from company").executeUpdate()
     }
res3: Int = 42

scala> // just checking

scala> DB.withConnection { implicit connection => SQL("select count(*) from computer").as(scalar[Long].single) }
res7: Long = 0

scala> DB.withConnection { implicit connection => SQL("select count(*) from company").as(scalar[Long].single) }
res8: Long = 0

scala> Company.options
res9: Seq[(String, String)] = List()

Ok, let’s create a couple of companies

scala> 
DB.withConnection { implicit connection => 
  Seq((1, "my Company"), (2, "my second company")).map { company =>
    SQL("insert into company values ( %s, '%s')".format(company._1, company._2) ).executeUpdate()
  }
}
res50: Seq[Int] = List(1, 1)

scala> Company.options
res51: Seq[(String, String)] = List((1,my Company), (2,my second company))

And a couple of computers

scala> val newComputer = Computer(NotAssigned, "my computer", None, None, Some(1))
newComputer: models.Computer = Computer(NotAssigned,my computer,None,None,Some(1))

scala> Computer.insert(newComputer)
res67: Int = 1

scala> Computer.insert(Computer(NotAssigned, "my second comuter", None, None, Some(2)))

scala> 
DB.withConnection { implicit connection =>
  SQL("select * from computer").as(Computer.withCompany *)
}
res8: List[(models.Computer, Option[models.Company])] = List((Computer(1000,my computer,None,None,Some(1)),None), (Computer(1001,my second comuter,None,None,Some(2)),None))

We’ll, this example is a little exagerated, but I guess you get an idea of what you can do with the play console, so keep hacking!

Primer Hackaton de Play! framework para Scala y Java en Argentina



Anotate en el primer Hackaton de Play! Framework en Argentina.

  • ¿Cuándo? Sábado 5 de Mayo, a las 9:30 am
  • ¿Dónde? Costa Rica 5546, Buenos Aires, Argentina

Nuestro objetivo en este primer meetup es que todos podamos conocer juntos Play! Framework 2.0, cada uno probando lo que mas le interesa de este framework, en el lenguaje que desea.

La idea es que cada uno que se anote, elija 2 de los 5 tópicos que le interesaría aprender, junto con un nivel de expertise en Play 2.0 y un lenguaje en el cual quiera realizar la experiencia. En base a esto, se crearán parejas para poder realizar Pair Programming durante todo el hackaton.

La dinámica del Hackaton será la siguiente:

  1. Registración
    1. La registración comenzará a las 09:30 AM y continuará hasta las 10. Mientras nos vamos registrando vamos a poder desayunar café con medialunas
  2. Charla inicial de Play! Framework
    1. Se dará una charla inicial de 30 a 45 minutos de Play! Framework 2.0 con ejemplos tanto en Scala como Java. Esta introducción nos permitirá tener a todos una idea básica de que es Play!
  3. División de proyectos
    1. En base a las personas que estamos en el Hackaton y a los resultados de la encuesta, se crearán diversos proyectos pequeños que abarquen estas ideas. Se separarán a todas las personas en estos proyectos de a pares, para poder realizar Pair Programming
  4. Trabajo en los equipos asignados hasta las 1730
  5. Almuerzo
    1. El almuerzo estará incluido. Se proveerán Pizzas con bebidas para que todos podamos comer mientras codeamos.
  6. Conclusiones
    1. Cada grupo debera preparar una mini charla de aproximadamente 10 minutos donde contarán los problemas y soluciones que se encontraron, mostrando un poco del código que hicieron

Para poder participar, es importante que traigas tu notebook y cuentes con:

  • Cuenta en GitHub
  • Conocimientos de Git
  • Conocimientos de Java y/o Scala

Aquí tienen información que pueden ir leyendo para ir informándose del tema:

Los esperamos :D

First Play! framework hackaton for Scala and Java in Argentina



Reserve your place at thefirst Play! framework Hackaton in Argentina.

  • ¿When? Saturday, May 5, 2012, 9:30 AM
  • ¿Where? Costa Rica 5546, Buenos Aires, Argentina

Our goal in this first meetup is to get to know Play! 2.0, focusing on what is more interesting for each of us, in the language we prefer.

Here you have the complete announcement in spanish.

Some useful resources:

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

¡Lanzamiento de Play 2.0!

Traducido del blog de Typesafe

¡Presentando Play 2.0!

En noviembre del año pasado anunciábamos que Play, el framework de alta productividad para Java y Scala, pasaría a formar parte del stack de Typesafe. Hoy, apenas unos meses más tarde, nos complace poder anunciar que Play 2.0 ya está disponible como parte del Stack Open Source de Typesafe en su versión 2.0, junto con el soporte comercial de la subscripción de Typesafe.

Veamos algunas de sus principales características.

Soporte nativo para Java y Scala

Mientras que la versión original de Play Framework estaba escrita principalmente en Java, ofreciendo soporte para scala a través de plug-in, Play 2.0 adopta Scala de una manera más decidida. No sólo el núcleo central de Play ha sido migrado a Scala, sino que esta nueva versión brinda un soporte de primer nivel para el desarrollo de aplicaciones web con Scala. De manera tal que la nueva versión del framework provee dos API totalmente completas: una para desarrolladores de Scala y otra para desarrolladores Java.

Un controlador hecho con Java

El mismo controlador usando Scala

Desarrollo rápido de aplicaciones

Una de las características que hacían a la “experiencia de usuario” del desarrollador Play de la serie 1.x, era la consola de desarrollo y el reporte de errores en el propio explorador web. Play 2.0 mejora estas prestaciones al permitirle al desarrollador ejecutar porciones de código, pruebas o scripts de la línea de comando interactuando directamente con el runtime de la aplicación, y también compilando muchas de las partes típicas de una aplicación web.

Interactuando con los modelos de una aplicación Play 2.0 desde la consola

Llevando la seguridad de tipos a un nuevo nivel

Una de las ventajas de utilizar un lenguaje estáticamente tipado para escribir aplicaciones de Play, es que el compilador podía verificar partes del código por usted, Es por ello que Play 2.0 utiliza por defecto un lenguaje de templates basado en Scala, incluso para aquéllos desarrolladores que usen Java como su principal lenguaje de programación. Esto no signifca que usted deba convertirse en un experto en Scala para escribir templates en Play 2.0. Pero ahí está Scala, sin que usted lo note, trabajando en su beneficio.

Play 2.0 lleva las verificaciones en tiempo de compilación y el chequeo de tipos todavía más a fondo. Las rutas (que definen los URLs y el mapeo con las acciones), los templates, e incluso los recursos de la aplicación web son compilados (utilizando LESS, CoffeScript y el compilador de Google Clousure), poniendo a su disposición un entorno de desarrollo unificado tanto para los desarrolladores del lado del cliente como para los del lado del servidor. El resultado de todo esto es que más errores serán detectados en tiempo de compilación, acelerando el proceso de desarrollo de aplicaciones web. También hará muchísimo más simple trabajar en proyectos grandes que involucren numerosos desarrolladores.

Example route compilation failure detection

Jugando limpio

Play 1.x implementaba muchas de sus originales ideas (como la implementación automática de propiedades para Java y la recarga dinámica de sus clases) utilizando técnicas que requerían un runtime específico para Play. En cambio, Play 2.0 adopta un enfoque mucho más estándar para el despliegue del runtime. Esto fue posible escribiendo parte del framework en scala, y también aprovechando prestaciones de SBT, la popular herramienta de Scala para la construcción y despliegue de aplicaciones.

Play 2.0 mantiene la misma experiencia de los usuarios de la versión 1.x acostumbrados a ejecutar “play new, run start”, al mismo tiempo que parte de una base más fácil de extender. Play 2.0 ya trae configurado un script de compilación que funcionará sin modificaciones en la mayoría de los casos, pero si usted necesita personalizar la manera en que su aplicación es construida y desplegada, tendrá las herramientas a su alcance para adapatarla a sus necesidades. Como resultado de todo esto, hallará que será mucho más simple desplegar sus aplicaciones de Play 2.0 en los escenarios más variados.

Elija tan sólo las partes que va a usar

Dado que el diseño de aplicaciones web ha evolucionado en gran medida en los últimos años, Play 2.0 está construido siguiendo un criterio modular que le dará gran flexibilidad a la hora de elegir la tecnología a usar. ¿No desea usar una base de datos? Puede simplemente deshabilitar el plugin DB. ¿Quiere usar su propio motor de templates? Simplemente agrégue el plugin. ¿No necesita un framework web con todos los chiches? Use play como una simple librería. Usted decide qué tanto de la arquitectura de Play quiere usar en sus aplicaciones.

Deshabilitando plugins en el archivo conf/application.conf

Escale su apliación con Akka

Play está basado en una arquitectura liviana, sin estado, pensada para la web, que le asegura un consumo de recursos mínimo y predecible (en términos de CPU, memoria, threads) para desarrollar aplicaciones que requieren alta escalabidad. Esto es posible en parte gracias a Akka 2.0, el middleware basado en eventos que se encuentra en el corazón de Play 2.0. Los desarrolladores tienen acceso a toda la funcionalidad provista por Akka para construir aplicaciones altamente distribuidas que puedan ser escaladas para cumplir con los más altos niveles de demanda.

En este ejemplo puede ver como el resultado de un actor encargado de realizar un cálculo complejo, puede ser mapeado a un resultado de Play de una manera no-bloqueante

En este ejemplo puede ver como el actor ChatRoom envía un mensaje en porciones al cliente utilizando Comet

Manejo avanzado de entrada/salida y de Streams

Una de las más recientes tendencias en el desarrollo de aplicaciones web es el énfasis puesto en tecnologías de tipo push y no-bloqueantes. Play 2.0 utiliza una implementación de entrada/salida basada en Iteratee, que provee soporte para tecnologías avanzadas de push y streaming, partiendo desde WebSockets y Comet hasta streaming de archivos.

Un controlador de scala que utiliza el soporte para WebSockets de Play 2.0

Listos… preparados… a jugar con Play!

Play 2.0 es el resultado de una intensa colaboración entre Typesafe (liderados por Peter Hausel), el fundador del proyecto Guillaume Bort, la comunidad Play y Zenexity, la consultora orientada a la web donde Play fue creado. Ha sido una experiencia divertida y excitante trabajar junto con un grupo tan talentoso de desarrolladores.

Estamos sumamente complacidos de que Play hoy sea parte del Stack de Typesafe, junto con Scala, Akka y el soporte comercial, mantenimiento y herramientas de despliegue y administración (como la nueva consola de Typesafe) que juntas conforman el paquete de Subscripción de Typesafe. Typesafe también ofrece cursos de capacitación y consultoría para la nueva versión 2.0, para que su equipo pueda cuanto antes comenzar a desarrollar aplicaciones con Play 2.0.

Estamos convencido de que el lanzamiento de Play 2.0 marcará un antes y un después en las comunidades de Java y Scala, poniendo al alcance de los desarrolladores una experiencia novedosa que posibilitará inéditos niveles de escalabilidad y productividad.

Para que pueda empezar ya mismo con Play 2.0, hemos provisto varias aplicaciones de ejemplo con esta nueva versión. Así que lo invitamos a verlo en acción para decidir por usted mismo, ¡y divertirse haciéndolo!

Ya que ha llegado hasta aquí, no pierda los anuncios recientes de la versión 2.0 del Stack de TypesafeAkka 2.0, y la Consola de Typesafe.

Para más información, lean el anuncio original en el blog de Typesafe

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: