Uso de DSL para minimizar la complejidad.

El objetivo de un DSL tal como lo define Martin Fowler es ser un lenguaje de programación con el puntual propósito de resolver un tipo de problema específico (a diferencia de un lenguaje de propósito general). Ejemplos de DSL son CSS, SQL o HQL (Hibernate Query Language).

Esta entrada no es para introducir los lenguajes de dominio específico, existen buenas referencias en la web, como el sitio de Martin Fowler con algunos artículos sobre DSL e incluso un libro. Esta entrada es para contar que nos motivó a usar DSL en un proyecto.

Se trata de un “catálogo de servicios”. Hay servicios de diferente naturaleza (Web Services, MQ, Stored Procedure, REST, etc). Al registrar un nuevo servicio necesitamos agregar metadata sobre las operaciones que pueden ejecutarse. La metadata puede ser usada por autómatas para generar invocaciones dinámicas de los diferentes servicios. Por ejemplo, un servicio de ruteo en una arquitectura ESB puede leer la metadata de un servicio MQ para saber en que cola colocar el mensaje y si además debe colocar información de contexto extra, etc.

En la forma clásica del diseño de la aplicación (el catálogo), uno pudiera crear un mantenedor de servicios donde la metadata es separada en objetos (Operaciones, Parámetros, Resultado, Excepciones, PreCondiciones, PostCondiciones, etc.) y usar algun CRM como Hibernate para generar la persistencia. Por otro lado tener una capa de servicios que permita generar la metadata desde estas operaciones (quizás usar flexjson para serializar a JSON el árbol de objetos que retorne Hibernate de la definición de los hbm (hibernate mapping). Pero esta es la vía complicada que nos obligan a tomar lenguajes antinaturales y antiexpresivos como java y todas las APIs y frameworks que se han gestado a su alrededor.

El diseño de lenguajes de programación como Ruby permite escribir código como:

class Comment < ActiveRecord::Base
    belongs_to :post
    validates_precence_of :body, :email, :author
end

Este código es un DSL escrito en Ruby en una forma natural (un comentario pertenece a un post y debe tener al menos body, email y autor).

En vez de usar un árbol de objetos complejos para definir la metadata y complicarnos la existencia con implementaciones CRM o código JDBC complejos, nuestro diseño se basó en las siguientes consideraciones:

  1. Usa un DSL interno para definir la metadata.
  2. El lenguaje usado para crear el DSL fue Javascript por ser dinámico (lenguajes dinámicos son mejores para escribir DSL), por ser expresivo, por ser sencillo; y principalmente porque puede ejecutarse de forma nativa en la web (donde corren la mayoría de las aplicaciones hoy en día).
  3. La notación usada para escribir la especifcación del DSL es JSON porque “…It is easy for humans to read and write. It is easy for machines to parse and generate…” y (personalmente por estar cansados de parsear XML).
  4. El DSL se inserta tal como viene en la columna de metadata.
  5. El cliente de los servicios de persistencia crea la metadata (siguiendo la especificación del DSL).
  6. El cliente de los servicios de persistencia es responsable de parsear el DSL en busca de datos (Por ejemplo el servicio de ruteo del ESB es reponsable de extraer la cola donde colocar el mensaje). En el caso de usar Javascript, el parseo es nativo por usarse JSON. Para el caso de Python[1] o Ruby[2] puede usarse un parser JSON (es sencillo por ser lenguajes dinámicos). En el caso de Java puede usarse Rinho para dar soporte Javascript y parsear en forma natural el DSL.

La especificación del DSL para el catálogo aún esta en proceso de definición. Actualizaré esta entrada con ejemplos del DSL y códigos de parseo cuando este listo.

Plus: Un excelente ejemplo del uso de DSL y Metaprogramming puede ser encontrado en este blog y su inspiración en esta librería.

[1].- SimpleJson o The Standard Python Library

[2].- JSON Implementation for Ruby.

Generar una respuesta JSON desde Java en UTF-8

He visto en varios forums que algunas personas se quejan de que cuando generan objetos en notación JavaScript (JSON) y que tienen caracteres del latín como los acentados (ñ, á, ü, etc) usando HttpServletResponse desde un servlet el browser les muestra caracteres extraños y preguntan por la solución.

La respuesta es sencilla. Antes de hacer el getWriter() del HttpServletResponse se debe setear el tipo de encoding que usarán para mapear los caracteres de forma que el browser los pueda interpretar y convertir de vuelta.

Hay dos formas de hacer esto:

1. Mediante el método setContentType(), que envía el tipo de contenido al cliente (browser) y donde puede setearse el charset.

response.setContentType("application/x-json;charset=UTF-8");
...

2. Mediante el método setCharacterEncoding(), que si es usado sobre-escribe la acción de setContentType().

response.setCharacterEncoding("UTF-8");
...

A continuación un ejemplo completo del envio de modelos de objetos en notación JavaScript hacia el browser desde un clase que hereda de org.springframework.web.servlet.view.AbstractView de Spring

...
    protected void renderMergedOutputModel(Map model, HttpServletRequest req,
            HttpServletResponse resp) throws Exception {
        JSONObject json = (JSONObject) model.get("json");
        resp.setCharacterEncoding("UTF-8");
        if (null != json) {
            resp.getWriter().write(json.toString());
        } else {
            resp.getWriter().write("{}");
        }
    }

Para mayor referencia, Tim Bray explica porque Unicode y UTF-8 son excelentes. Es una lectura obligatoria para entender de estos temas.

¿Por qué ProtocolBuffer si ya tenemos a JSON?

ProtocolBuffer

Leyendo el blog de un amigo supe de la existencia de ProtocolBuffer. Es un proyecto de Google para crear una forma nueva de serializar estructuras de datos en formato binario y usarlo en el intercambio de datos entre servicios remotos.

Para esto, se compila el archivo fuente que contiene el formato, lo cual genera codigo en el lenguaje destino que saben como parsear y generar el archivo de transferencia.

protoc -I=$SRC_DIR --java_out=$DST_DIR addressbook.proto

Sin duda es un buen proyecto, sobre todo para los que entendemos lo ineficiente que es usar XML como lenguaje de transferencia de datos.

JSON

Sin embargo me pregunto porque ProtocolBuffer y no JSON?.

JSON al igual que la idea de Google es un formato ligero de intercambios de datos creado por Douglas Crockford (Arquitecto de Yahoo !).

JSON es:

  1. Fácil de leer y escribir, explicito y expresivo.
  2. Fácil de parsear y generar por autómatas en diferentes lenguajes.
  3. Está basado en el lenguaje de programación JavaScript no obstante es language-agnostic.
  4. Usa notaciones que le son familiares a los programadores de los lenguajes más usados: Java, C, C++, C#, Python o Perl.
  5. Tiene una especificación creada por “Network Working Group”.
  6. Es el formato de intercambio de datos de facto en Ajax y la Web 2.0 .
  7. Existen parsers para casi todos los lenguajes: ASP, ActionScript, C, C++, C#, ColdFusion, D, Delphi, Erlang, Haskhell, haXe, Java, JavaScript, Lasso, Lisp, Lua, Objetive C, Perl, PHP, Pike, PL/SQL, PowerShell, Prolog, Python, Ruby, Squeak, Scala, Tcl, y otros.

Un ejemplo de un objeto JSON:

{
    "addressbook": {
        "name": "Mary Lebow",
        "address": {
            "street": "5 Main Street"
            "city": "San Diego, CA",
            "zip": 91912,
        },
        "phoneNumbers": ["619 332-3452", "664 223-4667"]
    }
}

Su versión XML:

<addressbook>
 <name>Mary Lebow</name>
 <address>
    <street>5 Main Street</street>
    <city zip="91912"> San Diego, CA </city>
    <phoneNumbers>
      <phone>619 332-3452</phone>
      <phone>664 223-4667</phone>
    </phoneNumbers>
 </address>
</addressbook>

JSON elimina la cantidad de tags de XML, lo que hace incluso más compacto los paquetes a serializar. Pero si solo fuera por esto, no valdría la pena el esfuerzo por crear JSON. Lo que lo hace más atractivo es que para la mayoría de los lenguajes acceder a los datos del objeto JSON es como acceder a atributos de un objeto en el lenguaje que se este usando, por ejemplo en JavaScript, Python o Ruby, acceder a la calle de la direccion del objeto anterior se escribe:

addressbook.address.street

O acceder al primer número telefónico:

addressbook.phoneNumbers[0]

Entonces, en vez de nuevos proyectos como BufferProtocol, me gustaría ver proyectos para crear herramientas o frameworks para intercambiar estructuras de datos en formato JSON entre diferentes lenguajes, que tal algo como:

jsonc -I=$SRC_DIR --java_out=$DST_DIR addressbook.json

y Obtener:

Address.java :

public class Address {
    private String street;
    private String city;
    private int zip;

    public String getStreet() {
        return this.street;
    }
    public String getCity() {
        return this.city;
    }
    public int getZip() {
        return this.zip;
    }
};

AddressBook.java:

public class AddressBook {
    private String name;
    private Address address;
    private String[] phoneNumbers;

    public String getName() {
        return this.name;
    }
    public void parseJSON(String json) {
        // llena address, phoneNumbers
    }
    public String writeJSON() {
        // retorna un objeto addressbook en formato JSON
    }

    public Address getAddress() {
        return this.address;
    }
    public String[] getPhoneNumbers() {
        return this.phoneNumbers;
    }
};

Esto nos permitiría parsear y generar los objetos JSON Address, y AddressBook en Java de forma natural.

Parsear los objetos:

AddressBook ab = new AddressBook();
ab.parseJSON(new FileInputStream("addressbook.json"));

O entonces generar JSON:

AddressBook ab = new AddressBook();
ab.setName("Mary Lebow");
...
String json = ab.writeJSON();

En XML tuvieramos que parsear el documento usando DOM, XPath, XQuery, etc.

Conclusión

JSON nos permite crear documentos con estructuras de objetos para la interoperabilidad de servicios en un formato entendible a simple vista, expresivo y sencillo. En Continuum seleccionamos JSON como estándar substituyendo (siempre que podamos) XML en todos los contextos donde es normalmente usado (configuración, intercambio, metadata, etc).

Sería interesante ver proyectos como ProtocolBuffer pero usando JSON como lenguaje de intercambio.

Conócenos

Tel: +56 2 9341951

e-mail: info@continuum.cl

Copyright © 2010 Continuum Ltda.
Coronel Pereira 72. Oficina 903. Las Condes. Santiago. Chile