Skip to content

JavaTodoList

Gilberth edited this page Sep 6, 2013 · 9 revisions
Clone this wiki locally

Su primer aplicación Play

Vamos a escribir una simple aplicación con Play 2.0 y desplegarla en la nube.

Prerequisitos

Antes que nada, asegúrese de tener una instalación de Play funcional. Solo necesita Java (al menos versión 6) y descomprimir los binarios de Play para empezar a trabajar; todo está incluído.

Como vamos a utilizar mucho la línea de comandos, es mejor use un sistema operativo semejante a Unix. Si usa Windows, también funcionará bien; solo que necesitará tipear algunos comandos en la terminal.

Necesitará al menos un editor de texto. Si desea usar un IDE para Java, como Eclipse o IntelliJ, por supuesto que podrá usarlo. Sin embargo, con Play va a divertirse más trabajando con un editor de texto simple como TextMate, Emacs o vi, gracias a que el framework se encarga de la compilación y el despliegue de la aplicación por sí mismo.

Creación del proyecto

Ahora que Play está instalado correctamente, es hora de crear una aplicación. Crear una aplicación Play es bastante fácil y todo el proceso puede llevarse a cabo usando las utilidades de línea de comandos de Play. Esto fomenta un esquema de proyecto estándar común a todas las aplicaciones Play.

Abra una terminal nueva y escriba:

$ play new todolist

La utilidad de Play le hará algunas preguntas. Escoja el template simple Java application.

El comando play new crea una nueva carpeta todolist/ con una serie de archivos y carpetas. Los más importantes son los siguientes:

  • app/ contiene el núcleo de la aplicación, repartido entre las carpetas para los modelos, los controladores y las vistas. Este directorio es donde residen los archivos de código fuente de Java.
  • conf/ contiene todos los archivos de configuración de la aplicación, especialmente el archivo principal application.conf, los archivos routes que definen las rutas y el archivo messages usado para la internacionalización de los mensajes.
  • project contiene los scripts de build. El sistema de build está basado en sbt. Cada nueva aplicación de Play viene provista con un script de build por defecto que funcionará sin necesidad de modificaciones en la mayor parte de los casos.
  • public/ contiene todos los recursos públicos, lo cual incluye archivos JavaScript, stylesheets y carpeta de imágenes.
  • test/ contiene todos los test de la aplicación. Los test pueden estar escritos como pruebas unitarias de JUnit.

Dado que Play usa UTF-8 como única codificación, es importante que todos los archivos guardados en esas carpetas usen esa codificación. Asegúrese de configurar adecuadamente su editor de texto.

Usando la consola de Play

Una vez que tenemos la aplicación creada, podemos ejecutar la consola de Play. Vaya a la nueva carpeta todolist/ y ejecute:

$ play

Esto inicia la consola de Play. Son varias las cosas que podemos hacer desde esta consola, pero empecemos por iniciar nuestra aplicación. Desde la consola, escriba run:

[todolist] $ run

Ahora la aplicación está corriendo en modo desarrollo. Abra el browser en http://localhost:9000/:

Nota: Lea más acerca de la consola de Play.

Visión General

Vamos a ver cómo hace la aplicación para mostrarnos esta página.

El punto de entrada principal de la aplicación es el archivo conf/routes. Este archivo define todas las URL accesibles desde su aplicación. Si abre el archivo verá esta primera ruta:

GET /       controllers.Application.index()

Esto simplemente le dice a Play que cuando el servidor reciba una solicitud GET para la dirección /, deberá llamar al método controllers.Application.index()

Veamos cómo son los métodos de controllers.Application.index. Abra el archivo todolist/app/controllers/Application.java:

package controllers;

import play.*;
import play.mvc.*;

import views.html.*;

public class Application extends Controller {

  public static Result index() {
    return ok(index.render("Your new application is ready."));
  }

}

Puede ver que controllers.Application.index() devuelve un Result. Todos los métodos de acción deben devolver un Result, que representa la respuesta a enviar al browser.

Nota: Lea más acerca de las Actions.

Aquí, el action devuelve un código HTTP 200 OK con una respuesta en HTML, provista por un template. Los templates de Play son compilados a métodos estándar Java, en este caso views.html.index.render(String message).

Este template está definido en el archivo app/views/index.scala.html:

@(message: String)

@main("Welcome to Play 2.0") {

    @play20.welcome(message)

}

La primera línea define la firma de la función, esto es la cantidad y tipos de parámetros que recibe. En este caso toma solamente un parámetro de tipo String. Luego el template combina contenido HTML (u otro contenido de texto) con código Scala. Las sentencias en Scala empiezan con el caracter @.

Nota: No se preocupe porque el motor de templates use Scala. No será un problema para un desarrollador Java, y podrá usarlo casi como si de Java se tratara.

Flujo de Trabajo durante el Desarrollo

Ahora vamos a hacer algunas modificaciones a la nueva aplicación. En Application.java cambiamos el contenido del response:

public static Result index() {
  return ok("Hello world");
}

Con este cambio, el action index responderá un text/plain Hello world. Para ver este cambio solo debemos refrescar la página de nuestro browser:

No necesita compilar el código o reiniciar el servidor para ver la modificación. Este se recargará automáticamente cuando se detecten cambios. ¿Pero qué pasa cuando comete un error en el código? Vamos a intentarlo:

public static Result index() {
  return ok("Hello world);
}

Ahora recargue la home page en su browser:

Como puede ver, los errores se muestran directamente en el navegador de una forma amigable.

Preparando la aplicación

Para nuestra aplicación de lista de tareas pendientes, necesitamos algunas actions y las correspondientes URLs. Empecemos por la definición de las direcciones o routes.

Modifique el archivo conf/routes:

# Home page
GET     /                       controllers.Application.index()

# Tareas
GET     /tasks                  controllers.Application.tasks()
POST    /tasks                  controllers.Application.newTask()
POST    /tasks/:id/delete       controllers.Application.deleteTask(id: Long)

Aquí hemos creado una ruta para listar todas las tareas, y otras que serán utilizadas para su crearlas y eliminarlas. La ruta para eliminarlas incluye una variable id en la URL, este valor es pasado al método deleteTask.

Ahora si refresca el navegador, verá que Play no puede compilar el archivo de rutas:

Esto es porque las rutas hacen referencia a métodos action que aún no existen. Así que vamos a agregarlos al archivo Application.java:

public class Application extends Controller {

  public static Result index() {
    return ok(index.render("Your new application is ready."));
  }

  public static Result tasks() {
    return TODO;
  }

  public static Result newTask() {
    return TODO;
  }

  public static Result deleteTask(Long id) {
    return TODO;
  }

}

Como puede ver retornamos TODO como resultado de la implementación de nuestras actions. Como aún no queremos implementar estas acciones podemos usar el resultado TODO para retornar una respuesta 503 Not Implemented.

Puede intentar acceder a http://localhost:9000/tasks para ver esto en acción:

Ahora lo último que necesitamos arreglar antes de empezar con la implementación de los métodos, es la acción index. Necesitamos que nos redireccione automáticamente a la página que nos muestra la lista de tareas:

public static Result index() {
  return redirect(routes.Application.tasks());
}

Como puede ver, usamos redirect en lugar de ok para retornar un tipo de respuesta 303 See Other. También hacemos uso de las rutas invertidas para obtener la URL correspondiente a la página que nos muestra la lista de tareas.

Nota: Lea mas acerca de Rutas y rutas invertidas.

Preparando el modelo de Tareas Task

Antes de continuar con la implementación necesitamos definir cómo representaremos una tarea, o Task, en nuestra aplicación. Para ello, crearemos una clase en el archivo app/models/Task.java:

package models;

import java.util.*;

public class Task {

  public Long id;
  public String label;

  public static List<Task> all() {
    return new ArrayList<Task>();
  }

  public static void create(Task task) {
  }

  public static void delete(Long id) {
  }

}

También hemos creado un grupo de métodos para manejar las operaciones que efectuaremos sobre las tareas. Por ahora vamos a escribir implementaciones vacías para cada operación, pero luego en este tutorial desarrollaremos las implementaciones que se encargarán de persistir las tareas en una base de datos relacional.

El template de la aplicación

Nuestra aplicación consistirá en una página muy simple que contendrá tanto la lista de las tareas almacenadas en nuestro sistema, así como un formulario para crear nuevas. Vamos a cambiar el template index.scala.html por:

@(tasks: List[Task], taskForm: Form[Task])

@import helper._

@main("Todo list") {

    <h1>@tasks.size() task(s)</h1>

    <ul>
        @for(task <- tasks) {
            <li>
                @task.label

                @form(routes.Application.deleteTask(task.id)) {
                    <input type="submit" value="Delete">
                }
            </li>
        }
    </ul>

    <h2>Add a new task</h2>

    @form(routes.Application.newTask()) {

        @inputText(taskForm("label")) 

        <input type="submit" value="Create">

    }

}

Cambiamos la firma del template para que acepte 2 parámetros:

  • Una lista de tareas a mostrar
  • Un formulario para ingresar nuevas tareas

También importamos helper._ para poder hacer uso de varias funciones que nos ayudarán a trabajar con formularios HTML, en especial la función form que crea el tag HTML <form> con los correspondientes atributos action y method, y la función inputText que crea el input HTML para los campos del formulario.

Nota: Lea más sobre el sistema de templates y las funciones para trabajar con formularios HTML.

El objeto Form contiene la definición del fomulario HTML, incluyendo las validaciones a realizar sobre los datos. Creemos un formulario para nuestra clase Task, agregando la siguiente línea al controlador Application:

static Form<Task> taskForm = Form.form(Task.class);

El tipo de dato de taskForm es Form<Task> ya que es un formulario que nos permite generar una tarea (Task). También necesitará importar play.data.*.

Podemos agregar las validaciones a aplicar a cada Task usando anotaciones JSR-303. Por ejemplo, hagamos que el campo label sea obligatorio:

package models;

import java.util.*;

import play.data.validation.Constraints.*;

public class Task {

  public Long id;

  @Required
  public String label;

  ...

Nota: Lea más acerca de Definición de formularios.

Renderizando la primer página

Ahora contamos con todos los elementos que precisamos para mostrar la página de la aplicación. Vamos a escribir las acciones de las tasks:

public static Result tasks() {
  return ok(
    views.html.index.render(Task.all(), taskForm)
  );
}

Esto retornará un resultado HTTP 200 OK y como contenido enviará el HTML devuelto por el template index.scala.html llamado con la lista de tareas y el formulario para dar de alta tareas.

Puede intentar acceder a http://localhost:9000/tasks en su navegador:

Manejando el envío del formulario

Por el momento, si ingresamos una nueva tarea, seguiremos obteniendo la página TODO. Vamos a implementar el método newTask:

public static Result newTask() {
  Form<Task> filledForm = taskForm.bindFromRequest();
  if(filledForm.hasErrors()) {
    return badRequest(
      views.html.index.render(Task.all(), filledForm)
    );
  } else {
    Task.create(filledForm.get());
    return redirect(routes.Application.tasks());  
  }
}

Usamos bindFromRequest para crear un nuevo formulario con los datos de la solicitud. Si ocurriera cualquier error en el formulario, volveremos a mostrar el mismo (retornando un 400 Bar Request en vez de 200 OK) para que el usuario corrija la información. Si no hay errores, creamos la tarea y redireccionamos a la lista de tareas.

Nota: Lea más sobre la Presentación de formularios.

Persistiendo las tareas en base de datos

Para que nuestra aplicación sea de utilidad, debemos persistir las tareas en la base de datos. Empezaremos habilitando una base de datos en nuestra aplicación. En el archivo `conf/application.com, agregamos:

db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"

Por ahora vamos a usar una simple base de datos H2 en memoria. No necesitamos reiniciar el server, con refrescar el browser será suficiente para iniciar la base de datos.

En este tutorial usaremos EBean para hacer consultas a la base. Así que tendrá que habilitarlo en su archivo application.conf:

ebean.default="models.*"

De esta manera estaremos creando un servidor Ebean conectado al datasource por defecto, que se encargará de controlar todas las entidades del package models. Ahora transformaremos nuestra clase Task en una entidad válida de Ebean:

package models;

import java.util.*;

import play.db.ebean.*;
import play.data.validation.Constraints.*;

import javax.persistence.*;

@Entity
public class Task extends Model {

  @Id
  public Long id;

  @Required
  public String label;

  public static Finder<Long,Task> find = new Finder(
    Long.class, Task.class
  );

  ...

Hicimos que la clase Task extienda de la super clase play.db.ebean.Model para tener acceso a la funcionalidad de Ebean provista por Play. También agregamos las anotaciones de persistencia y creamos un objeto find para ejecutar las consultas.

Implementemos las operaciones CRUD (alta, consulta, actualización y baja):

public static List<Task> all() {
  return find.all();
}

public static void create(Task task) {
  task.save();
}

public static void delete(Long id) {
  find.ref(id).delete();
}

Ejecute de nuevo la aplicación, debería poder crear nuevas tareas sin problemas.

Nota: Lea mas acerca de Ebean.

Eliminando tareas

Ahora que ya podemos crear tareas, necesitamos poder eliminarlas. Muy simple, solo necesitamos terminar la implementación de la acción deleteTask:

public static Result deleteTask(Long id) {
  Task.delete(id);
  return redirect(routes.Application.tasks());
}

Desplegando la aplicación en Heroku

Toda la funcionalidad de nuestra aplicación ya está lista. Es hora de ponerla en producción. Despleguémosla en Heroku. Primero tiene que crear un archivo Procfile para Heroku en el directorio raíz de la aplicación:

web: target/start -Dhttp.port=${PORT} -DapplyEvolutions.default=true -Ddb.default.url=${DATABASE_URL} -Ddb.default.driver=org.postgresql.Driver

Nota: Lea mas sobre Desplegar aplicaciones en Heroku.

Usaremos las propiedades de sistema para sobreescribir la configuración de la aplicación cuando esté corriendo en Heroku. Dado que Heroku provee un motor de bases de datos PostgreSQL tendremos que agregar el driver a las dependencias de nuestra aplicación.

Para ello, deberemos agregar al archivo project/Build.scala:

val appDependencies = Seq(
  "postgresql" % "postgresql" % "8.4-702.jdbc4"
)

Nota: Lea mas sobre Manejo de dependencias.

Heroku usa git para poner en producción su aplicación. Vamos a inicializar el repositorio git:

$ git init
$ git add .
$ git commit -m "init"

Ahora podemos crear la aplicación en Heroku:

$ heroku create --stack cedar

Creating warm-frost-1289... done, stack is cedar
http://warm-1289.herokuapp.com/ | git@heroku.com:warm-1289.git
Git remote heroku added

Y luego pondremos nuestra aplicación en producción con un simple git push heroku master:

$ git push heroku master

Counting objects: 34, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (20/20), done.
Writing objects: 100% (34/34), 35.45 KiB, done.
Total 34 (delta 0), reused 0 (delta 0)

-----> Heroku receiving push
-----> Scala app detected
-----> Building app with sbt v0.11.0
-----> Running: sbt clean compile stage
       ...
-----> Discovering process types
       Procfile declares types -> web
-----> Compiled slug size is 46.3MB
-----> Launching... done, v5
       http://8044.herokuapp.com deployed to Heroku

To git@heroku.com:floating-lightning-8044.git
* [new branch]      master -> master

Heroku se encargará de hacer el build de nuestra aplicación y la desplegará en un nodo en algún lugar de la nube. Puede verificar el estado del proceso con el siguiente comando:

$ heroku ps

Process       State               Command
------------  ------------------  ----------------------
web.1         up for 10s          target/start

Ya está, ahora puede verla en acción desde el navegador.

¡Su primer aplicación ya está corriendo en producción!

Something went wrong with that request. Please try again.