domingo, 11 de enero de 2015

Mapeo entre Beans en tiempos de JSON. Como mapear entre JSON y beans de Java usando Jackson y Dozer


Recientemente he tenido la necesidad de integrar un nuevo servicio externo datos en formato JSON y tener que acomodarlos en el conglomerado de beans de Java ya existente previamente en la aplicación que lo usaría. Estos últimas clases tienen una estructura definida hace años y reciben datos de muchas otras fuentes. El nuevo JSON difiere en estructura, pero debe de convertirse en uno de los viejos objetos Java sin que nadie lo note.

{

"mapid":333,

"mapNombre":"Jose Perez",
"mapRelated":[
"A",
"B",
"C"
],
"mapInternalA":{
"mapInternalA":"id1",
"mapInternalA1":{
"theMostInternal":4
}
},
"mapcompactA":{
"id":"fgfg",
"name":"gfdgfd"
},
"mapB":3424,
"mapC":23
}


Para el mapeo JSON-Java usamos Jackson librería potente y rápida, pero que sin embargo para nuestro caso actual presenta un problema. No esta preparada para modificar la estructura de los datos que esta mapeando. Esto viene a querer decir que teniendo un JSON, se necesitaría tener un objeto Java completamente igual para poder efectuar el mapeo. 

//No es este objeto el que necesitaba

public class A {

Integer id;
String name;
List<String> related;
InternalA internalA;
CompactInternalA compactA;
}


Dado que mi caso no era así, estaba claro que necesitaríamos dos fases de mapeo, esta ya mencionada y una segunda usando Dozer que si que es una librería especializada en mapear dos beans de Java no importa lo distintos que sean. Solo necesita un archivo XML que indique las equivalencias entre ellos para poder hacer su magia. 

<mapping type="one-way">

<class-a map-get-method="getValue">es.santescas.NestedAccessMap</class-a>

<class-b>es.santescas.dto.A</class-b>
<field>
<a key="mapid">this</a>
<b>id</b>
</field>
<field>
<a key="mapNombre">this</a>
<b>name</b>
</field>
<field>
<a key="mapRelated">this</a>
<b>related</b>
</field>
<field>
<a key="mapInternalA.mapInternalA">this</a>
<b>internalA.internalA</b>
<a-hint>java.lang.String</a-hint>
</field>
<field>
<a key="mapInternalA.mapInternalA1.theMostInternal">this</a>
<b>internalA.internalA1.theMostInternal</b>
</field>
<field map-id="CompactReaalyInternalA">
<a key="mapcompactA">this</a>
<b>compactA</b>
<a-hint>java.util.Map</a-hint>
<b-hint>es.santescas.dto.CompactInternalA</b-hint>
</field>
</mapping> 


Todo esto nos dejaba con un proceso mas o menos así.



Sin embargo la idea de mantener la información de mapeo en 2 lugares distintos no me gustaba. Si algún campo de la fuente cambiaba no solo sería necesario cambiar el conjunto de beans 1 sino que también el archivo XML. Los beans 1 debián morir.

En este punto entra en juego una cualidad de Dozer, quizás poco explorada y es que es casi tan potente convirtiendo entre dos objetos beans de Java como entre un mapa y un objeto. Usando esto fue posible eliminar el conjunto de objetos A y tener definido información del mapeo solo en el archivo XML.



El código fuente del ejemplo completo puede ser encontrado en:
Repo de Github

miércoles, 29 de octubre de 2014

Nueva aplicación en el Google Play Store. Paletris




Recientemente he publicado en el Google Play Store mi nueva aplicación. Esta vez se trata de un juego, que tiene parte de puzzle y parte de agilidad y que a algunos podrá recordarles al famoso Tetris pero en realidad funciona de manera muy distinta.

En el mismo, deberás de acomodar bloques con letras dentro que van cayendo del cielo de manera que cuando los tengas en tierra puedas formar palabras uniendo algunos de ellos y ganando puntos, siguiendo hasta cierto punto para esto la lógica del Scrabble (formar una palabra con la ñ es mucho mas complicado que con la a, y en consecuencia deben de ser los puntos que se otorguen). Toda la acción la guía un gracioso Rinoceronte, fruto del trabajo del diseñador gráfico Abdel de la Campa.

Tecnológicamente hablando esta creado usando Android nativo y apoyándose en la librería RoboGuice. Además, hace uso del listado de palabras obtenidas con el método descrito aqui, desde el código fuente de Chrome.




Inyectando valores en las vistas en Android al estilo Roboguice o ButterKnife.

A la hora de desarrollar aplicaciones Android, soy un gran fanático de Roboguice. Este fue el que nos permitió pasar de

class EjemploActivity extends Activity {
TextView nombre;
 
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        nombre      = (TextView) findViewById(R.id.nombre); 

        //Utilizar findViewById con cast incluido para obtener el TextView
    }
}


a

class EjemploActivity extends RoboActivity {
    //Sencilla anotación que realiza la misma función

    @InjectView(R.id.nombre)             
    TextView nombre;
 
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

o sea, eliminar un montón de código de inicialización de nuestros vistas por medio de una simple anotación que le indica al framework que debe de hacerlo por nosotros. Lo malo de todo es que me gustó tanto, que al final acabé queriendo aplicar su filosofía a todo. Si no podía hacerlo como RoboGuice ya no estaba tan bien. Por ejemplo muchas veces me encontré queriendo cambiar la fuente de una vista y para esto tenía que hacer:

Typeface fuenteDeseada=Typeface.createFromAsset(getAssets(),"roboto");
nombre.setTypeface(fuenteDeseada);


Para esto me cree este proyecto. Con esta mini-librería es posible definir una anotación y en adición a esta definir una clase que implemente la funcionalidad esperada al encontrar declarada la anotación. De esta forma, después de haber declarado TypefaceInjector, podía tranquilamente hacer:

@InjectView(R.id.nombre)
 @InjectTypeface(typefaceName="roboto")
 private TextView nombre;

Y de manera similar con eventos y con muchas cosas más.

Tiempo después de desarrollar esto descubrí ButterKnife, cuyo código fuente es mucho mas sencillo de extender que RoboGuice y además tiene la particularidad que para el rellenado de las vistas utiliza generación de código en la compilación en lugar de Reflection, con la consecuente mejora de rendimiento. De haber conocido ButterKnife un poco antes, mi decisión hubiese sido extender este en lugar de crear SantaGuice, porque creo que este framework es realmente muy prometedor.

lunes, 20 de octubre de 2014

Creando e inicializando objetos complejos con datos de prueba

Te has encontrado alguna vez ante una clase como la siguiente:

public class Container {

List<ContentA> list1;
ContentB[] list2;
ContentC c3;
}

public class ContentA {

String strContent;
ContentB contentB;
}

public class ContentB {

int intContent;
ContentC cc;
}

public class ContentC {

Date dateContent;
}

Y te ves en la necesidad de instanciar un objeto de tipo Container con todos sus miembros con un cierto valor (puede ser aleatorio) para usarlo con cualquier fin, desde pasar un test hasta mockear la salida de un WebService que devuelve un POJO similar.

Yo he debido de hacerlo muchas veces, al principio solo tenía la opción de la "fuerza bruta", o sea

Container container=new Container();
container.setList1(new ArrayList());
container.setList2(new ContentB[5]);
container.setContentC(new ContentC());

container.getList1.add(...)
....

Tedioso no? Al tiempo me implemente una clase mía basada en la clase PropertyUtils de Apache Commons con las cuales inicializaba dinamicamente los objetos uno a uno, que represento un cierto avance con respecto a la solucion inicial pero todavia no era lo ideal.

Lo ideal lo descubrí en la libreria Podam la cual, a pesar de tener un interminable numero de configuraciones posibles para inicializar un objeto de la manera que mas nos guste, con dos sencillas lineas nos crea un nuevo objeto Container con todas sus propiedades rellenas tal y como queríamos.

PodamFactory factory = new PodamFactoryImpl();
//Usando la estrategia aleatoria de creación de objetos

Container container= factory.manufacturePojo(Container.class);

Y ya está. Simple no?

Comprobar si un número es impar, ¿Cual es la manera mas rápida?

Mi lectura de turno es el aunque medio añejo, muy recomendable libro Java Puzzlers . En este, los autores, que en su mayoría han estado ligados al desarrollo del lenguaje Java en alguna de sus fases nos plantean ejercicios que siempre parece que terminan de una forma y lo hacen de otra, sorprendiéndonos gratamente, desvelándonos alguna de las interioridades del lenguaje y haciéndonos conocerlos un poco mejor.

Uno de los ejercicios gira en torno a conocer en Java si un número es impar. La manera usual de obtener esto es mediante el operador % usando la expresión siguiente:

numero%2!=0

Este operador, en realidad calcula el resto de la división de numero entre 2 y por consiguiente de acuerdo a la especificación del lenguaje seria parte de la siguiente igualdad

numero/2+resto=división

a partir de la cual es calculado.

En Java Puzzlers se nos plantea la alternativa de saber si un número es impar si al aplicarle una operación Bitwise And con 1 como segundo operando devuelve algo distinto de 0

numero & 1!=0

El fundamento de esto es que el operador And Bitwise realiza un And entre las representaciones binarias de ambos operandos. El 1 al representarse en binario solo podrá dar como resultado un numero distinto de 0 al aplicarle un & Bitwise al ser comparado con un numero que también tenga un uno en la ultima posición en binario y estos numero tienen la forma 2n+1 o sea son impares.

000000001
&
________1
=
000000001

Para comprobar la veracidad del planteamiento se ha elaborado un mini programa que dado una entrada de 1 000 000 de números pone en practica ambos métodos para comprobar si son impares y el resultado como podrá verse comprueba lo planteado por los autores.

Ejecutando comprobacion con el mismo valor, 1000000 times
Via usual
Tardo 6 milisegundos
Via Bitwise
Tardo 0 milisegundos
Ejecutando comprobacion con 1000000 valores diferentes
Via usual
Tardo 5 milisegundos
Via Bitwise
Tardo 1 milisegundos



La ligera diferencia que existe entre el caso en el que los números son todos distintos y cuando son iguales no parece repetirse en sucesivos ejecuciones del programa por lo que no debería ser significativa. De esto puede extraerse que aunque usar el bitwise and pueda significar confuso a primera vista para posibles terceros que lean nuestro código, en ocasiones puede significar una mejora importante en el rendimiento de nuestras aplicaciones y es sin duda una cuestión que merece ser conocida por todos los amantes de un código eficiente y optimo.

lunes, 4 de agosto de 2014

Creando un Realm personalizado para Shiro que permita leer los usuarios de una fuente externa


Como ya hemos vistos en alguno de los post anteriores Apache Shiro es una buena herramienta para las tareas de seguridad mas comunes dentro de una aplicación J2EE. Una de las formas que tiene este framework para permitirnos definir toda la configuración de la seguridad de nuestra aplicación de una manera declarativa y externa a la misma es mediante la creación de un archivo .ini con la siguiente estructura.

[main]
authc.loginUrl = /jsp/login.xhtml
authc.successUrl = /jsp/index.xhtml

[users]
admin=LECTOR
admin=EDITOR

[roles]
LECTOR=nota:leer
EDITOR=nota:crear,modificar

[urls]
/jsp/listaNotas.xhtml=perms["nota:leer"]
/jsp/insertarNota.xhtml=roles["EDITOR"]
/jsp/** = authc

Sin embargo, con frecuencia es posible rellenar de antemano todas las secciones anteriores excepto la de los usuarios. Es muy común que en las aplicaciones de hoy en día obtengamos la información de los usuarios a partir de otro tipo de recursos tales como una Base de Datos LDAP que sin duda nos brindan una mayor flexibilidad para el manejo de los cambios en los mismos (altas y bajas), así como mayor seguridad que un simple archivo de texto. ¿Pero que pasaría si no quisiésemos renunciar a la idea de definir nuestra configuración de seguridad en un archivo .ini pero necesitásemos obtener los usuarios de una fuente externa?

Excavando en la muy bien diseñada arquitectura de Shiro encontramos el concepto de Realm que es una interfaz para dado el nombre y contraseña del usuario actual recuperar la información de autenticación o autorización del mismo, dada que se cuenta con que esta almacenada en un sitio externo. Shiro posee varias clases que implementan esta interfaz para los casos mas comunes tales como :

  • AbstractLdapRealm
  • ActiveDirectoryRealm
  • JdbcRealm
  • JndiLdapRealm
  • IniRealm


Este ultimo es el Realm autorizado en el caso en que usemos un archivo ini. Mirando en el código fuente vemos que su funcionamiento interno consiste en parsear el archivo y extraer cada una de las secciones. Es de esta forma que nos es posible reutilizar esta clase a través de la herencia, pero sustituir en nuestra clase hija el fragmento en el que se cargan los usuarios por nuestra propia forma de obtenerlos. Un ejemplo sería como el que sigue:

...
public class IniConUsuarioExternoRealm extends IniRealm {

private static final String INI_FILE = "/WEB-INF/shiro.ini";

@Override
protected AuthenticationInfo doGetAuthenticationInfo

(AuthenticationToken token) throws AuthenticationException {
actualizarDatosDeUsuario();
return super.doGetAuthenticationInfo(token);
}

@Override
protected void onInit() {
this.setResourcePath("classpath:" + INI_FILE);

super.onInit();
}

public void actualizarDatosDeUsuario

(AuthenticationToken token) {
Map<String, Object> map = FacesContext.getCurrentInstance().

getExternalContext().getSessionMap();

String user = token.getUser();
String pass = token.getPs();
String[] roles = //Aqui obten los roles para tu usuario 

//de donde quiera que los tengas

if (StringUtils.isBlank(user)) {
throw new AuthorizationException("No hay usuarios ni roles");
}

construirCuenta(user, "", roles);
}

private SimpleAccount construirCuenta

(String username, String password, String[] roles) {
SimpleAccount account = getUser(username);
if (account == null) {
account = new SimpleAccount(username, password, getName());
add(account);
}

account.setCredentials(password);

if (roles != null) {
for (int i = 0; i < roles.length; i++) {
String rolename = roles[i];
account.addRole(rolename);

SimpleRole role = getRole(rolename);
if (role != null) {
account.addObjectPermissions(role.getPermissions());
}
}
} else {
account.setRoles(null);
}

return account;
}
}