jueves, 20 de julio de 2017

CRUD con Play Framework + Scala + PostgreSQL

¿Qué es Scala?

Scala es un lenguaje de programación multi-paradigma diseñado para expresar patrones comunes de programación en forma concisa, elegante y con tipos seguros. Integra sutilmente características de lenguajes funcionales y orientados a objetos. La implementación actual corre en la máquina virtual de Java y es compatible con las aplicaciones Java existentes.

más información en:
https://www.scala-lang.org/

¿Qué es Play Framework?

Play es un framework de aplicación web de código abierto, escrito en Scala y también utilizable desde otros lenguajes de programación compilados a Bytecode, p. Java (Play incluye una API de Java en versiones más recientes), que sigue el patrón de arquitectura model-view-controller (MVC). Su objetivo es optimizar la productividad del desarrollador mediante el uso de convenciones sobre la configuración, la recarga de código en caliente y la visualización de errores en el navegador

Scala y Play son una combinación perfecta para desarrollar aplicaciones que requieran de mucha demanda o millones de transacciones o peticiones por segundo, su diseño stateless (Sin estado) permite que Play Framework con Scala sean los elegidos para ambientes Cluster y que requieran de alto performance gracias a que también permiten operaciones asíncronas en el lado del servidor.

¿Qué necesitaremos para realizar nuestro CRUD?


- IntelliJ IDEA Community Edition
- Una computadora con Linux, Windows, Mac OSX o cualquier SO compatible.
- Instalar SBT http://www.scala-sbt.org/0.13/docs/es/Installing-sbt-on-Mac.html
- Necesitaras utilizar alguna plantilla, te recomiendo la Starter https://github.com/playframework/play-scala-starter-example/tree/2.6.x

La estructura de directorios final de nuestro proyecto será la siguiente:


Comenzamos

El primer paso será configurar nuestro proyecto es decir indicar las rutas y acceso a bases de datos. Esto lo realizaremos en los archivos conf/application.conf, conf/routes y build.sbt.


Configurando build.sbt

Para que nuestro proyecto se conecte apropiadamente utilizando JDBC necesitamos agregar las siguientes dependencias.

libraryDependencies += jdbc
libraryDependencies += "org.postgresql" % "postgresql" % "9.4-1206-jdbc42"

Ej.

Configurando Application.conf

El application.conf es un archivo muy útil ya que permite definir ciertas configuraciones como la conexión a base de datos de nuestro proyecto.
Ej.

Configurando routes.

El archivo routes será el que nos permitirá definir las rutas de acceso a nuestros controladores, los importantes en este proyecto son:

 style="background-color: white; font-family: Menlo; font-size: 9pt;"># Home page
GET     /                       controllers.TaskController.list
# Tasks
GET     /tasks                  controllers.TaskController.list
POST /newTask                   controllers.TaskController.newTask
GET /addTaskView                   controllers.TaskController.addTaskView
GET /updateTaskView/:id/:label     controllers.TaskController.updateTaskView(id: Long, label: String)
POST    /deleteTask/:id      controllers.TaskController.deleteTask(id:Long)
POST    /tasks/update/:id/:label       controllers.TaskController.updateTask(id: Long, label: String)

Las rutas anteriores contienen las operaciones básicas de alta, modificación y baja de datos en 
la tabla "task" que será objeto de uso en nuestro ejemplo.

Creando nuestro modelo.


Play Framework utiliza el patrón de diseño modelo - vista - controlador (MVC) por lo tanto
iniciaremos creando nuestro modelo.

Para ello en el directorio models crearemos una clase Scala llamada Task.scala.



package models

import play.api.db._
import anorm._
import anorm.SqlParser._
import javax.inject.{Inject, Singleton}
 case class TaskModel(id: Long, label: String)

@Singletonclass Task @Inject() (dBApi: DBApi){
  private val db = dBApi.database("default")
  val task = {
    get[Long]("id") ~
      get[String]("label") map {
      case id ~ label => TaskModel(id, label)
    }
  }

  def all(): List[TaskModel] = db.withConnection { implicit c =>
    SQL("select * from task").as(task *)
  }

  def create(label: String) {
    db.withConnection { implicit c =>
      SQL("insert into task (label) values ({label})").on(
        'label -> label
      ).executeUpdate()
    }
  }

  def update(id: Long, label: String){
    db.withConnection{ implicit c =>
      SQL("UPDATE task SET label = {label} WHERE id = {id}").on(
        'id -> id,
        'label -> label
      ).executeUpdate()
    }
  }

  def delete(id: Long) {
    db.withConnection { implicit c =>
      SQL("delete from task where id = {id}").on(
        'id -> id
      ).executeUpdate()
    }
  }
}

Creando nuestras vistas.


Luego procedemos a crear nuestras vistas en el directorio "views". Para ello utilizaremos
una plantilla basada en el archivo main.scala.html.

Archivo main.scala.html.

@*
 * This template is called from the `index` template. This template
 * handles the rendering of the page header and body tags. It takes
 * two arguments, a `String` for the title of the page and an `Html`
 * object to insert into the body of the page.
 *@
@(title: String)(content: Html)
<!DOCTYPE html>
<html lang="en">
    <head>
        @* Here's where we render the page title `String`. *@
        <title>@title</title>
        <link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/bootstrap-theme.min.css")">
        <link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/bootstrap.min.css")">
        <link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/main.css")">
        <script src="@routes.Assets.versioned("javascripts/jquery-3.2.1.min.js")" type="text/javascript"></script>
        <script src="@routes.Assets.versioned("javascripts/bootstrap.min.js")" type="text/javascript"></script>
    </head>    <body>        @* And here's where we render the `Html` object containing
         * the page content. *@
        @content
    </body></html>


Archivo addTask.scala.html.


@(taskForm: Form[String])(implicit messages: Messages)
@import helper._
@main("Welcome to Play") {
@*
* Get an `Html` object by calling the built-in Play welcome
* template and passing a `String` message.
*@
<div id="main">
    <h2>Agregar una nueva nota</h2>

    @form(routes.TaskController.newTask) {
    <div class="form-group">
        <label for="label">Nota</label>
        <input type="text" class="form-control" name="label" id="label" required="required" placeholder="Ingresar la nota">
    </div>
    <div class="form-group">
        <input type="submit" class="btn btn-default" value="Crear">
        <input type="reset" class="btn btn-default" value="Cancelar">
    </div>
    }
    <a href='@routes.TaskController.list'>Show list</a>
</div>
}

Archivo tasks.scala.html

@(tasks: List[TaskModel])(implicit messages: Messages, req: RequestHeader)
@import helper._
@main("Welcome to Play") {
@*
* Get an `Html` object by calling the built-in Play welcome
* template and passing a `String` message.
*@
<div id="main">
<h1>@tasks.size tasks</h1>
<div class="add">
    <a href='@routes.TaskController.addTaskView'><button type="button" class="btn btn-primary">Agregar</button></a>
</div>
    <div class="table-responsive">
        <table class="table">
            <tr>
            <th>id</th><th>label</th><th>Update</th><th>Delete</th>
            </tr>
            @tasks.map { task =>
            <tr>
                <td>@task.id</td>
                <td>@task.label</td>
                <td><a href="@routes.TaskController.updateTaskView(task.id,task.label)"> <button type="button" class="btn btn-info">Actualizar</button></a></td>
                <td><button type="button" data-id="@task.id" id="btn-modal-delete" class="btn btn-danger" data-toggle="modal" data-target="#myModal">Eliminar</button></td>
            </tr>
            @*routes.TaskController.deleteTask(task.id)*@
            }

        </table>
    </div>
    <!-- Modal -->    <div id="myModal" class="modal fade" role="dialog">
        <div class="modal-dialog">

            <!-- Modal content-->            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal">&times;</button>
                    <h4 class="modal-title">Modal Header</h4>
                </div>
                <div class="modal-body">
                    <p>Esta seguro en eliminar el registro?</p>
                    <input type="hidden" id="data-id"/>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-primary" id="btnEliminar" data-dismiss="modal">Aceptar</button>
                    <button type="button" class="btn btn-default" data-dismiss="modal">Cerrar</button>
                </div>
            </div>

        </div>
    </div>
</div>
<script type="text/javascript">

$(document).on('click','#btn-modal-delete', function(e){
    var taskId = $(this).data('id');
    console.log("taskId:" + taskId);
    $('.modal-body #data-id').val(taskId);
});

$('.modal-dialog .modal-footer').on('click','#btnEliminar', function(e) {
    var id = $('.modal-body #data-id').val();
    console.log(id);
    $.ajax({
        type: 'POST',
        url: '/deleteTask/'+ id,
        success: function(data) {
            document.open();
            document.write(data);
            document.close();
        }
    });
    return false;

});
</script>
}

Archivo updateTaskView.scala.html.

@(id: Long, label: String)(implicit messages: Messages)
@import helper._
@main("Welcome to Play") {
@*
* Get an `Html` object by calling the built-in Play welcome
* template and passing a `String` message.
*@
<div id="main">
    @form(routes.TaskController.updateTask(id, label)) {
    <input type="text" value="@id" name="id" class="form-control">
    <input type="text" value="@label" name="label" class="form-control">
    <input type="submit" class="btn btn-default" value="Update">
    }
</div>

}

Con esto ya tenemos listas nuestras vistas. Ahora procederemos a crear nuestros controladores

Creando nuestros controladores.

Para nuestro ejemplo sencillo solo crearemos un controlador llamado TaskController. En nuestro
controlador definiremos las diferentes acciones que realizaremos con nuestro modelo y vistas.
Los controladores por lo general deben ir en el directorio "controllers".
package controllers
import javax.inject.{Inject, Singleton}

import forms.TaskForm
import models.Task
import play.api.mvc.{Action, Controller}
import play.api.i18n.{I18nSupport, MessagesApi}

@Singletonclass TaskController @Inject()(val messagesApi: MessagesApi)(task: Task) extends Controller with I18nSupport {

  def list = Action {implicit request =>
    Ok(views.html.tasks(task.all()));
  }

  def addTaskView = Action {
    Ok(views.html.addTask(TaskForm.getForm));
  }

  def updateTaskView(id: Long, label: String) = Action {
    Ok(views.html.updateTaskView(id, label));
  }

  def newTask = Action { implicit request =>
    TaskForm.getForm.bindFromRequest.fold(
      errors => BadRequest(views.html.tasks(task.all())),
      label => {
        task.create(label)
        Redirect(routes.TaskController.list)
      }
    )
  }

  def updateTask(id: Long, label: String) = Action { implicit request =>
    TaskForm.getForm.bindFromRequest.fold(
      errors => BadRequest(views.html.tasks(task.all)),
      label => {
        task.update(id, label)
        Redirect(routes.TaskController.list)
      }
    )
  }

  def deleteTask(id: Long) = Action {request =>
    task.delete(id);
    Redirect(routes.TaskController.list)
  }

}

Listo. Ahora procederemos a crear nuestra base de datos en PGAdmin 4
para PostGreSQL. Crearemos una base de datos llamada "test" y a modo de ejemplo también
crearemos una tabla llamada "task" con dos campos llamados "id" y "label".



Luego nada más nos resta iniciar el servidor Netty, para lo cuál abrimos la consola o terminal
nos dirigimos a el path del proyecto y haremos uso de los comandos:

sbt : Para iniciar Scala Build Tools
clean: para limpiar el proyecto
compile: Para compilar nuestro proyecto
run: para iniciar el servidor de aplicación.














si probamos nuestro localhost puerto 9000 podremos notar nuestro CRUD funcionando a la perfección, cabe aclarar que se utilizaron otras herramientas como Bootstrap y JQuery Las cuales pienso explicar en otro post.

Espero les sea de utilidad y recuerden si desean un tema especifico solo escriban en los comentarios y yo procedo a hablar al respecto. También agradecerles a quienes colaboran vía PayPal y con los anuncios para que este blog siga vigente. Hasta el siguiente post.


viernes, 9 de junio de 2017

Importancia del uso de Synchronized al implementar Threads (Hilos) en Java

Un bloque o método sincronizado en Java es aquel que utiliza la palabra reservada synchronized.

La importancia en su uso radica en evitar o prevenir las condiciones de carrera(race condition) al manejar hilos en Java.

La palabra reservada Synchronized.

Los bloques sincronizados en Java están marcados con la palabra clave synchronized. Un bloque sincronizado en Java se sincroniza en algún objeto. Todos los bloques synchronized sincronizados en el mismo objeto sólo pueden tener un subproceso ejecutándose dentro de ellos al mismo tiempo. 

Todos los demás hilos que intentan entrar en el bloque sincronizado se bloquean hasta que el hilo dentro del bloque sincronizado sale.

La palabra reservada synchronized puede utilizarse para cuatro tipos de bloques diferentes:
Metodos de instancia, metodos estáticos, bloques de código dentro de metodos de instancia, bloques de código dentro de metodos estáticos. 

El tipo de bloque synchronized que necesites dependerá de una situación concreta.

Ejemplo.


He aquí un ejemplo que inicia 2 subprocesos y que ambos llaman al método agregar en la misma instancia de Cobro. Sólo un hilo a la vez podrá llamar al método agregar en la misma instancia, ya que el método se sincroniza en la instancia a la que pertenece.

public class Cobro{
     
     long contar = 0;
    
     public synchronized void agregar(long valor){
       this.contar += valor;
     }
  }

public class CobroThread extends Thread{

     protected Cobro cobro = null;

     public CobroThread(Cobro cobro){
        this.cobro = cobro;
     }

     public void run() {
 for(int i=0; i<10; i++){
           cobro.agregar(i);
        }
     }
  }

  public class Ejemplo {

    public static void main(String[] args){
      Cobro cobro = new Cobro();
      Thread  threadA = new CobroThread(cobro);
      Thread  threadB = new CobroThread(cobro);

      threadA.start();
      threadB.start(); 
    }
  }

Se crean dos subprocesos. La misma instancia de cobro se pasa a ambos en su constructor. El método agregar() se sincroniza en la instancia, porque el método agregar es un método de instancia y se marca como sincronizado. Por lo tanto, sólo uno de los subprocesos puede llamar al método agregar() a la vez. El otro hilo esperará hasta que el primer hilo deje el método agregar(), antes de que pueda ejecutar el método mismo.

Si los dos subprocesos hubieran hecho referencia a dos instancias de cobro distintas, no habría problemas para llamar a los métodos agregar() simultáneamente. Las llamadas habrían sido a objetos diferentes, por lo que los métodos llamados también estarían sincronizados en diferentes objetos (el objeto que posee el método). Por lo tanto las llamadas no bloquearían. Aquí es cómo podría verse:

 public class Ejemplo {

    public static void main(String[] args){
      Cobro cobroA = new Cobro();
      Cobro cobroB = new Cobro();
      Thread  threadA = new CobroThread(cobroA);
      Thread  threadB = new CobroThread(cobroB);

      threadA.start();
      threadB.start(); 
    }
  }

 Observe cómo los dos subprocesos, threadA y threadB, ya no hacen referencia a la misma instancia de cobro. El método agregar de cobroA y cobroB se sincroniza en sus dos instancias propietarias. Llamar agregar() en cobroA no bloqueará así una llamada a agregar() en cobroB.

A modo de ejemplos de alto nivel si no usáramos synchronized cuando manipulamos hilos podría darse el caso que multiples hilos alteren el estado o las características de un mismo objeto causando problemas como los siguientes

Un sistema mal diseñado de reserva de entradas podría ocasionar que dos usuarios accediendo desde la web a la vez, reserven legítimamente la misma butaca. O un sistema de conteo automático de plazas libres de un parking en sus barreras de entrada y salida de vehículos podría indicar que está totalmente vacío cuando realmente está lleno (o viceversa). De igual forma, dos personas ingresando y retirando efectivo a la vez de una misma cuenta bancaria podrían ver su saldo incrementado o por el contrario su ingreso realizado pero no materializado en saldo disponible. Si bien son situaciones que pudieran ser improbables, son posibles y pueden ser y han de ser evitadas en el desarrollo de software.

Si los procesos que están en condición de carrera (race condition) son correctamente sincronizados, todo debería funcionar correctamente, por lo que el resultado será el esperado. Múltiples procesos se encuentran en condición de carrera si el resultado de los mismos depende del orden de su llegada y si no son correctamente sincronizados, puede producirse una corrupción de datos, que puede derivar incluso en un problema de seguridad del sistema capaz de ser explotado de forma malintencionada. Análogamente, en circuitos electrónicos se da una condición de carrera cuando la salida de un sistema o subsistema depende del orden en que se hayan mandado las solicitudes de activación o desactivación de sus componentes.


Recuerda que este blog sobrevive gracias a tus donaciones o clics en los anuncios, ya sabes como apoyar este recurso para continuar subiendo mas tutoriales. Gracias.