martes, 19 de abril de 2016

Spark con Scala. Perparación del entorno de desarrollo y ejemplo sencillo

Al empezar a trabajar con Scala, uno de los retos que nos encontramos es el de la preparación de un entorno que nos facilite la vida y agilice los tiempos de nuestros desarrollo. 
 En este post voy a presentar dos maneras de trabajar con Scala para el desarrollo de de aplicaciones para Apache Spark, una sin IDE y otra con IDE

En primer lugar, la forma mas sencilla y directa de crear una primera aplicación con Scala es la de escribirla directamente con un editor de textos. En mi caso, he elegido SublimeText y he creado la siguiente aplicación de ejemplo:


Con guardar el archivo con la extensión .scala tendremos suficiente.

Luego deberemos descargar Sbt (Super BuildTool), en mi caso para windows pero también existen versiones para Linux y Mac. Sbt es la herramienta estandard para construir aplicaciones de Scala.

Crearemos una estructura de directorios del siguiente tipo:

./simple.sbt
./src
./src/main
./src/main/scala
./src/main/scala/SimpleApp.scala

Crearemos el archivo .sbt de la siguiente forma :

name := "Simple Project"

version := "1.0"

scalaVersion := "2.10.5"

libraryDependencies += "org.apache.spark" %% "spark-core" % "1.6.1"
Y generaremos la aplicación con sbt package esto nos creara un .jar de salida en la ruta ./target

Para ejecutar nuestro jar en Spark, simplemente lo haremos con el comando :

$ YOUR_SPARK_HOME/bin/spark-submit \
  --class "SimpleApp" \
  --master local[4] \
  target/scala-2.10/simple-project_2.10-1.0.jar

Todo el ejemplo detallado se puede encontrar en la página de la documentación oficial de spark


  • ECLIPSE


Si lo que queremos es un entorno de desarrollo para crear este tipo de aplicaciones, todos aquellos que vengamos del binomio Java + Eclipse agradeceremos tener un IDE basado en eclipse para el desarrollo de aplicaciones Scala, se puede descargar de aquí.

Ahora bien, si lo que queremos es construir la aplicación de Scala con Maven desde Eclipse, deberemos instalar un plugin. Aquí os dejo la url del repositorio :

http://alchim31.free.fr/m2e-scala/update-site/

Una vez instalado para crear un nuevo proyecto haremos: New --> Other --> Maven Project

y seleccionamos el archetype Scala-archetype-simple


Alternativamente,

si el método anterior no funciona lo haremos de la siguiente manera:

Crearemos un Maven Project

Click derecho sobre el proyecto y seleccionamos Configure --> Add Scala Nature

En el pom.xml enganchamos 


 
  
   
   
    org.scala-tools
    maven-scala-plugin
    
     
      compile
      
       compile
      
      compile
     
     
      test-compile
      
       testCompile
      
      test-compile
     
     
      process-resources
      
       compile
      
     
    
   
   
    maven-compiler-plugin
    
     1.7
     1.7
    
   
   
   
    org.apache.maven.plugins
    maven-assembly-plugin
    2.4
    
     
      jar-with-dependencies
     
    
    
     
      assemble-all
      package
      
       single
      
     
    
   
   
    org.apache.maven.plugins
    maven-jar-plugin
    
     
      
       true
       fully.qualified.MainClass
      
     
    
   
  
  
   
    
    
     org.eclipse.m2e
     lifecycle-mapping
     1.0.0
     
      
       
        
         
          org.scala-tools
          
           maven-scala-plugin
          
          
           [2.15.2,)
          
          
           compile
           testCompile
          
         
         
          
         
        
       
      
     
    
   
  
 

Nos mostrara un error, en la pestaña de Problems --> click derecho sobre el error Quick Fix

A continuación, en el archivo pom.xml, importamos las librerías de spark


     
      org.apache.spark
      spark-core_2.10
      1.6.1
    
  
 

Ahora se nos mostrará otro error, causado porque el Scala IDE pone como defecto para construir el proyecto Scala 2.11 y el spark-core espera scala 2.10, buildPath y cambiamos la versión de Scala.

Ahora cambiamos la carpeta src de java a scala y ya habremos terminado la configuración.

Finalmente esta es la pinta que tendrá nuestro proyecto



Ya solo quedará generar el jar haciendo Run as --> Maven build y en mi caso en el parámetro Goal poniendo clean install (por ejemplo).



miércoles, 24 de febrero de 2016

WS REST Recepción de datos con el cliente

Siguiendo los dos post anteriores (Sencillo WebService REST con Jersey framework y WS REST Representación de objetos Java como JSON) sobre el desarrollo de WS REST, en esta entrada voy a profundizar un poco mas la manera de recibir la información que nos viene del WS con un cliente hecho en java

Básicamente voy a mostrar dos maneras, en el caso en que el WS nos devuelva un JSON y lo queramos tratar como tal en el cliente, este es el bloque de código adecuado

ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
Client client = Client.create(clientConfig);
WebResource webResource = client.resource("http://localhost:8080/WS_SMSReceiver/SmsService/getSmsMessage");
ClientResponse response = webResource.accept("application/json").type("application/json").get(ClientResponse.class);
String output = response.getEntity(String.class);
JSONObject jsonObject = new JSONObject(output);

En el caso, altamente útil que ya vimos en el post anterior, que el WS nos devuelva una lista de objetos que nostros hayamos definido previamente, en formato JSON, el código para recibirlo y tratarlo será el siguiente
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
Client client = Client.create(clientConfig);
WebResource webResource = client.resource("http://localhost:8080/WS_SMSReceiver/SmsService/getSmsMessage");
List list = webResource.accept("application/json").type("application/json").get(new GenericType>(){});

jueves, 4 de febrero de 2016

WS REST Representación de objetos Java como JSON

El objetivo de esta entrada es el describir la manera mas elegante de integrar el Web Service REST hecho con jersey en el articulo anterior para la comunicación mediante protocolo JSON con el cliente.

Vamos a mapear el objeto Java para que cada vez que hagamos referencia a el dentro de un contexto de nuestro servlet este se convierta automaticamente a formato JSON, para ello lo primero que haremos es modificar el archivo descriptor (web.xml) y le añadiremos las siguentes lineas.
Ahora crearemos una clase de la siguiente forma, es importante incluir un método sin argumentos y sobreescribir el metodo toString()

public class SendingData {

 private String date;
 private String valor;
 
 public SendingData() {
 }
 
 public SendingData(String date, String valor) {
  
  this.date = date;
  this.valor = valor;
 }
 public String getDate() {
  return date;
 }
 public String getValor() {
  return valor;
 }
 public void setDate(String date) {
  this.date = date;
 }
 public void setValor(String valor) {
  this.valor = valor;
 }

 @Override
 public String toString() {
  return new StringBuffer(" date : ").append(this.date)
    .append(" value : ").append(this.valor).toString();
 }
 
}
A partir de aquí, ya podremos devolver los métodos de nuestro servicio haciendo referencia directamente una instancia del objeto (o una lista de objetos de este tipo), el servicio del artículo anterior queda así :


@Path("/SmsService")
public class SmsService {
 
 @GET
 @Path("/ping")
 public Response getServerTime() {
  System.out.println("RESTful Service 'MessageService' is running ==> ping");
  return Response.status(200).entity("received ping on " + new Date().toString()).build();
 }
 
 @Path("/smsSend/{message}")
 @GET
 @Consumes (MediaType.APPLICATION_JSON)
 @Produces(MediaType.APPLICATION_JSON)
 public SendingData smsSend(@PathParam("message") String message) throws ParseException{
  
  SendingData sendingData = new SendingData();
  sendingData.setDate(new Date().toString());
  sendingData.setValor("valor de ejemplo");
  
  return sendingData;
  
 }
 
 @Path("/smsSendList/{message}")
 @GET
 @Consumes (MediaType.APPLICATION_JSON)
 @Produces(MediaType.APPLICATION_JSON)
 public List smsSendList(@PathParam("message") String message) throws ParseException{
  List list = new ArrayList();
  SendingData sendingData = new SendingData();
  sendingData.setDate(new Date().toString());
  sendingData.setValor("valor de ejemplo");
  list.add(sendingData);
  sendingData = new SendingData();
  sendingData.setDate(new Date().toString()+ "//2");
  sendingData.setValor("valor de ejemplo 2");
  list.add(sendingData);
  return list;
  
 }
}
Y las llamadas de esta manera:

http://localhost:8080/WS_SMSReceiver/SmsService/smsSend/

{"date":"Thu Feb 04 16:44:13 CET 2016","valor":"valor de ejemplo"}

http://localhost:8080/WS_SMSReceiver/SmsService/smsSendList/

[{"date":"Thu Feb 04 16:51:53 CET 2016","valor":"valor de ejemplo"},{"date":"Thu Feb 04 16:51:53 CET 2016//2","valor":"valor de ejemplo 2"}]


Siguiendo con la mismo filosofía, si queremos hacer la llamada desde un cliente (aplicación Java, por  ejemplo) para mapear al objeto como si fuese un JSON, haremos lo siguiente. En primer lugar copiaremos al otro proyecto la clase que representa los datos ( SendingData()).

Crearemos en nuestro servicio del WS (SmsService) un método para recibir el objeto que nos envía el cliente mediante POST (también se podría hacer por GET, pero así variamos un poco). El método quedaría de esta forma :

@Path("/sendData")
 @POST
 @Consumes (MediaType.APPLICATION_JSON)
 @Produces(MediaType.APPLICATION_JSON)
 public Response  sendData(SendingData sd) throws ParseException{
  
  String sendingDataJSON = sd.toString();
  System.out.println(sendingDataJSON);
  return Response.status(200).entity("ok").build();
  
 }
Ahora implementamos una aplicación Java sencilla, donde en el main implementaremos las siguientes lineas de código (no olvidar importar las librerías de Jersey)

SendingData sendingData = new SendingData();
  sendingData.setDate(new Date().toString());
  sendingData.setValor("valor de ejemplo cliente");   
   
  ClientConfig clientConfig = new DefaultClientConfig();

  clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
  Client client = Client.create(clientConfig);
  WebResource webResource = client.resource("http://localhost:8080/WS_SMSReceiver/SmsService/sendData");
  ClientResponse response = webResource.accept("application/json").type("application/json").post(ClientResponse.class, sendingData);

  if (response.getStatus() != 200) {
   System.out.println("ERROR!!!!!!!!!");
  }else{
   String output = response.getEntity(String.class);
   System.out.println("Output from Server .... "+output);
  }
El resultado es que ahora el cliente puede enviar el objeto como si fuese un JSON al WS y este lo recibirá como si fuese el mismo objeto, cosa que nos ahorra estar haciendo parsers o cualquier otra filigrana.

Sencillo WebService REST con Jersey framework

En esta entrada voy a crear un Web Service Rest muy sencillo, que servirá como base a desarrollos mas complejos, con ayuda del framework Jersey. Para este ejemplo he utilizado la version 1.x

En primer lugar, descargamos las librerías necesarias de aqui.

Una vez añadidas al proyecto creamos una clase principal para atender las peticiones, en mi caso SmsService


package com.nextret.smsreceiver.services;


import java.util.Date;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.json.JSONObject;

@Path("/SmsService")
public class SmsService {
 
 @GET
 @Path("/ping")
 public String getServerTime() {
  System.out
    .println("RESTful Service 'MessageService' is running ==> ping");
  return "received ping on " + new Date().toString();
 }
 
 @Path("/smsSend/{message}")
 @GET
 @Produces("application/json")
 public String smsSend(@PathParam("message") String message){
  JSONObject json = new JSONObject();
  json.put("Clave 1", "Valor 1");
  json.put("Clave 2", "Valor 2");
  return json.toString();
  
 }
 
 @Path("/tst")
 @GET
 @Produces(MediaType.APPLICATION_JSON)
 public Response tst(){
  JSONObject json = new JSONObject();
  json.put("Clave 1", "Valor 1");
  json.put("Clave 2", "Valor 2");
  return Response.status(200).entity(json.toString()).build();
  
 } 
}
Y un descriptor web.xml


  WS_SMSReceiver
  
        WS_SMSReceiver
        com.sun.jersey.spi.container.servlet.ServletContainer
         
            jersey.config.server.provider.packages
            com.nextret.smsreceiver.services
         
         1
    
    
        WS_SMSReceiver
        /*
    

Desplegamos en un Tomcat y introducimos la url :

http://localhost:8080/WS_SMSReceiver/SmsService/ping

Veremos la salida como : "received ping on Thu Feb 04 14:10:12 CET 2016"

A continuación paso a explicar un poco el código.
El servicio SmsService, que esta definido por  @Path("/SmsService"), atiende una serie de peticiones que son cada uno de los métodos implementados en esta clase.
Por un lado tenemos un simple metodo llamado ping, al que se accede mediante la ruta : @Path("/ping"). Tiene como objetivo simplemente devolver la hora para comprobar que el WS se encuentra levantado y escuchando.

Por otro lado, y a modo de ejemplo, se han implementado dos métodos mas. Por un lado el método tst() ( @Path("/tst")) devuelve un objeto JSON al cliente con un codigo 200.

El método smsSend (@Path("/smsSend/{message}")) también devuelve un Json per esta vez prescindiendo del objeto Response, simplemente con un String, en este caso, el método acepta un parámetro para su posterior tratamiento en el mismo