blog de ezamudio

Spring Batch, más allá del tutorial

Parte de la operación de un sistema en el que llevo ya años trabajando, consiste en generar varios reportes diarios para conciliar con proveedores. El proceso es automatizado y ya hay componentes reutilizables para realizar esto, pero la cantidad de productos últimamente ha crecido bastante y esto trae un problema de desempeño, pues cada reporte lee la misma tabla, pero con criterios diferentes, para obtener prácticamente el mismo tipo de datos: Las ventas de X producto de un día.

La tabla en cuestión contiene las ventas de todos los productos, de modo que lo que ocurre diariamente es que se realiza la misma consulta, una vez por producto; algo así como SELECT * FROM venta WHERE producto=? y solamente cambia el producto. Si tenemos 20 productos, pues son 20 consultas a la tabla.

Comunicación asíncrona entre procesos Java

En este post de jpaul estuve comentando acerca de algunas de las broncas de RMI, y de cómo hay opciones más eficientes para cuando se necesita implementar comunicación eficiente entre dos aplicaciones Java, usando algo similar a RMI.

Primero que nada, quiero mostrar cuál es el problema concreto con RMI: cada llamada que se recibe en la aplicación que publica el componente, se hace en un hilo separado. Para demostrar esto, tomé el ejemplo original y lo modifiqué un poco: El servidor tarda un poco de tiempo, y también imprime el nombre del hilo actual y el número total de hilos activos:

Random rng = new Random(System.currentTimeMillis())

Remote stub = UnicastRemoteObject.exportObject(new TestRemote() {
  @Override
  public String sayHello(String name) throws RemoteException {
    println "Corriendo en ${Thread.currentThread().name} total ${Thread.activeCount()}"
    if(rng.nextBoolean())Thread.sleep(10)
    return "Hello, " + name;
  }
}, 0);
def registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
registry.bind("Test", stub);
println "Escuchando..."

Varianza en Ceylon

Bueno pues después del artículo maratónico acerca de varianza en Java, ahora quiero explicar cómo se implementó esto en Ceylon. Para ello vamos a usar los mismos ejemplos, de modo que quede clara la comparación.

Lo primero son las tres clases para el ejemplo, y la primera función:

class A() {}
class B() extends A() {}
class C() extends B() {}

B f(B param) => C();

Hasta aquí, todo funciona exactamente igual que en Java:

B algo;
A a = f(algo); //OK
B b = f(algo); //OK
C c = f(algo); //Error

f(A()); //ERROR
f(B()); //OK
f(C()); //OK

En Ceylon también existe la covarianza en los tipos de retorno, similar a lo que se introdujo en Java 5:

class Padre() {
  shared default A metodo() => A();
}
class Hijo() extends Padre() {
  shared actual C metodo() => C();
}

Con los parámetros no se puede hacer algo similar. Ceylon no tiene sobrecarga de métodos, pero no tiene contravarianza en los parámetros; por lo tanto, al refinar un método de un supertipo, los parámetros deben ser exactamente del mismo tipo que en la declaración original, o el compilador emite un error.

Varianza, Covarianza y Contravarianza

En los sistemas de tipado estático, existe este concepto de varianza, que a veces puede entenderse muy fácil pero tampoco es tan intuitivo como parece.

Para ilustrar la varianza, vamos a definir una jerarquía de clases muy simple:

public class A {}
public class B extends A {}
public class C extends B {}

Y definimos un método o función que usa estos tipos:

public B f(B param) {
   //Qué puede devolver?
}

Primero que nada, viéndolo desde fuera, ¿A qué tipo de variables podemos asignar lo que devuelve esta función?

B algo;
A a = f(algo);
B b = f(algo);
C c = f(algo);

Las primeras dos líneas son correctas, la tercera da un error. Esto es porque la función devuelve un valor de tipo B, y B es también un A. Pero no podemos asignar a una subclase de B, al menos no sin hacer un cast, pero no vamos a hacer casts en esta ocasión. B no es un C, por lo tanto la tercera línea no compila.

Hasta aquí todo bien. Ahora, ¿Qué le podemos pasar como argumento a esta función? Tiene un solo parámetro de tipo B. En esta ocasión, es alrevés:

f(new A()); //ERROR
f(new B()); //OK
f(new C()); //OK

Hystrix: primer contacto

En la conferencia de Software Guru de este año, Agustín Ramos dio una charla acerca de sistemas tolerantes a fallas, en la cual mencionó un software que me llamó mucho la atención, llamado Hystrix, desarrollado por Netflix.

La idea de Hystrix es que en sistemas que se comunican mucho con otros sistemas por medio de red, poder aislar todas esas llamadas a servicios externos y permitir que sean administradas de forma robusta, es decir, que haya un control de conexiones salientes, mantener buenos tiempos de respuesta, con tolerancia a fallas integrada.

Esto suena muy bien: si tengo un sistema que hace llamadas constantes a un web service externo, generalmente el funcionamiento de ese web service afecta el funcionamiento de mi sistema: Si de repente se pone lento, se tarda mucho en contestar, mi sistema empieza a sentirse lento, porque está esperando respuesta del sistema externo. Luego empiezan los problemas porque resulta que mi sistema encola las llamadas a dicho web service, precisamente para no saturarlo, pero pues está lento y eso está fuera de mi control pero resulta que las llamadas encoladas ya se tardan mucho tiempo en ejecutarse, es decir, la tardanza del web service no solamente se convierte en esperar respuesta del mismo, sino que hay llamadas que se quedan mucho tiempo encoladas y entonces puede que ya salgan muy tarde. ¿Y si hay un usuario en línea esperando la respuesta? ¿Y si el sistema ya le respondió error, incluso antes de que siquiera se realice la llamada al servicio externo?

Java 8: mis primeros tropiezos

Pues ahora que ya salió oficialmente Java 8, lo instalé y empecé a hacer pruebas, con un proyecto grande que ya llevo desde hace varios años (empezó en Java 1.4 y lo he migrado a Java 5, 6, 7 y ahora seguirá la 8).

Cuando he migrado, las etapas suelen ser así:

Primero correr las aplicaciones tal cual están, sin modificar nada, sobre la nueva JVM. Eso parece que está funcionando bien (pero tendrán que estar en observación varios días obviamente).

Luego, compilar el código tal cual está, sin modificar nada, con el nuevo compilador. Aquí es donde me di de topes ya desde ahorita. Pasaron dos cosas:

Utilizo Lombok para reducir código en varios lugares y facilitarme la vida en general cuando programo en Java. Una de las cosas que trae es la anotación @SneakyThrows, que permite tratar una excepción verificada como si fuera RuntimeException al momento de compilar. Pero al compilar código que utiliza esta anotación en Java 8:

post-compiler 'lombok.bytecode.SneakyThrowsRemover' caused an exception: null

Y esto es usando la versión 1.12.6 que se supone ya trae soporte para Java 8.

Clases internas en Groovy

Acabo de toparme con un problema en Groovy. Tristemente la solución que todo mundo da en foros es simplemente "no lo hagas, es mejor si haces [cualquier otra solución]"; pero, qué tal si lo que necesitaba era esto?

Bueno y ¿cuál es el problema? Es simple: el soporte en clases internas tiene problemas con atributos heredados en la clase externa. Tiene solución, y es sencilla, pero me parece una leaky abstraction y honestamente la encontré por pura suerte, buscando maneras de darle vuelta al asunto.

El problema se da bajo estas circunstancias:

//Una clase padre, normalita, con un atributo normalito
class Parent {
  String name
}
//Una subclase, normalita, con una clase interna
class External extends Parent {
  //Un atributo propio de esta clase
  String outer
  //Y esta es la clase interna
  class Internal {
    //También tiene un atributo propio
    String inner
    //Este método truena como ejote
    void boom() {
      //Esta línea se ejecuta bien, se despliega el atributo externo y el interno
      println "Outer is " + outer + ", inner is " + inner
      //Pero esta línea truena con un error macabro,

Web Services en Axis2 con Spring y Gradle

Recientemente en un proyecto un cliente nos dijo que la interfaz entre nuestros sistemas sería... esperen... sí... un web service. Un web service que ellos invocarían y que por lo tanto nosotros tenemos que implementar. Así que nos dieron un par de archivos WSDL y unos ejemplos de XML para asegurarnos que salga como debe ser (no entiendo para qué, si todas las herramientas de generación de web services se encargan de que el XML siempre salga como debe ser, ¿verdad?).

Cuando tengo que invocar un web service, generalmente uso Axis2. Me ha resultado de lo más sencillo porque consiste simplemente en ejecutar el script wsdl2java dándole el URI del WSDL y con eso tengo una clase que puedo usar para invocar los servicios (junto con mil clases internas que envuelven los datos dentro de envoltorios intermedios porque parece que los de Axis entendieron OOP como Onion-Oriented Programming). Así que en esta ocasión decidí probar el otro lado de la implementación: publicar un servicio hecho con Axis2.

Comparativo de desempeño para formato de fechas

Comparto esto para los que tengan la misma duda que yo tenía: Saber si es más rápido usar String.format o un SimpleDateFormat para formatear una fecha.

Es claro que String.format puede ser más simple de codificar, sobre todo en ambientes concurrentes, ya que SimpleDateFormat no es thread-safe y por tanto hay que usarlo solamente de manera local o bien envolverlo en un ThreadLocal.

Pero pues hice un programa muy simple en Groovy para comparar el performance de ambos:

import java.text.SimpleDateFormat

SimpleDateFormat df = new SimpleDateFormat('yyyyMMddHHmmss')
Date now = new Date()
//calentamiento
10000.times {println df.format(now)}
10000.times {println String.format('%tY%<tm%<td%<tH%<tM%<tS', now)}
System.currentTimeMillis()

long t0=System.currentTimeMillis()
10000.times {
    df.format(now)
}
long t1 =System.currentTimeMillis()
10000.times {
    String.format('%tY%<tm%<td%<tH%<tM%<tS', now)
}
long t2 = System.currentTimeMillis()
println "SimpleDateFormat: ${t1-t0} vs String.format: ${t2-t1}"

Game Of Life en Ceylon

En el Code Retreat que hubo en Agosto de 2012, el reto fue implementar el famoso Game of Life, en parejas. En mi equipo, @juwe y yo lo desarrollamos en Ceylon (no me impuse; él se animó, jejej).

Apenas hoy lo pude subir a un repositorio que tenemos en GitHub especialmente para ejemplos de Ceylon, después de darle una buena actualizada al código porque han habido varios cambios en la sintaxis y en el módulo de lenguaje desde que escribimos esto originalmente: ya teníamos comprensiones, funciones de orden superior, funciones anónimas y varias otras cosas, pero no teníamos por ejemplo el assert, tuplas, ni la sintaxis actual para secuencias y colecciones iterables; la sintaxis para funciones anónimas cambió ligeramente, al igual que la de parámetros variádicos (los famosos varargs).

Distribuir contenido