viernes, 6 de diciembre de 2013

El truco del brazo peludo

Hace un par de años trabajaba como arquitecto de una aplicación para una empresa importante cuyo responsable técnico (llamémosle Eladio) era probablemente una de las personas más insufribles que haya conocido en mi vida profesional. No merece la pena perder tiempo en describirlo con detalle (quizás en otra entrada del blog), baste con decir que tenía el "don" de sacar de sus casillas a todas y cada una de las treinta personas de nuestro equipo con la que tuvo que tratar en algún momento.

Pues bien, cierta semana estaba prevista una demo de la aplicación para un pez gordo de la empresa. El nerviosismo y la presión de Eladio eran mayúsculos y más aún cuando a un día de la temida demo apareció en la cabecera de todas las páginas la imagen de un zorro en lugar del logo corporativo. Sí, un zorro: animal de la familia de los cánidos.

Al equipo de desarrollo nos pareció gracioso. A Eladio, no.



No fue un cambio deliberado: probablemente un programador hizo una prueba en el entorno que no debía. El caso es que el pequeño zorro seguía ahí, justo al principio de cada página de la aplicación. No era muy grande, ni contenía un mensaje malsonante, ni ocupaba más de 100 píxeles de ancho... joder, ¿pero qué hacía un zorro ahí?

Eladio estaba hecho un basilisco despotricando contra todo responsable de mi empresa que se le cruzara.

La solución era trivial y no llevaba nada de tiempo: bastaba con reemplazar el zorro (al que ya le habíamos cogido cariño) por el logo de siempre. ¿Cuánto podría llevar arreglarlo, cinco minutos? Sin embargo, como todo el equipo andaba hasta arriba con temas mucho más complejos que Eladio había exigido que funcionaran para la demo, nadie asumió la tarea de recuperar el logo. Así pues, a pesar de haber sido la comidilla de toda la oficina, el zorro continuó alegrando las páginas de la aplicación.

El día de la demo, cuando Eladio estaba al borde del colapso, despedimos a nuestro amigo el zorro y volvimos a poner el logo anterior.



Si la prueba de fuego para Eladio era la demo, para nosotros lo era el minucioso test que él iba a realizar antes sobre la aplicación. Cuál fue nuestra sorpresa cuando observamos cómo Eladio pasaba una a una las páginas de la aplicación casi de puntillas, sin detectar errores, sin entrar en el detalle que nos tenía acostumbrados: "a este banner le falta un píxel", "esa imagen se ha cargado un segundo antes que la anterior", "en la versión en español he visto un mensaje en inglés", "este vídeo tarda mucho en cargar", ...

A él lo que le importaba es que ya no saliera el zorro por ninguna parte y el zorro ya no apareció. Eladio suspiró aliviado, la aplicación estaba lista para pasar la demo, y el equipo feliz porque no se había detectado ningún fallo, ¡ninguno!

Parece ser que aplicamos sin quererlo "el truco del brazo peludo" que se basa en meter un error muy llamativo para el cliente con el fin de que éste sólo se fije en dicho gazapo y no repare en lo demás.

¡Hasta siempre otra, amigo zorro!

(Recuerdo evocado tras la lectura de este post de Yorokobu)

miércoles, 13 de octubre de 2010

JSF - ajaxSingle

'ajaxSingle' es un atributo de algunos componentes JSF de A4J (AJAX for JSF) de RichFaces que indica si el componente para que se aplica debe (true) o no (false) ser incluido en la lista de componentes que será procesada en el ciclo de vida de JSF.

Con este atributo se evitan fallos en la fase de validación de aquellos componentes que no nos interesa procesar en una petición AJAX. Así pues, sólo se procesan los componentes con 'ajaxSingle = true'.

JSF - a4j:region

es una etiqueta JSF de la librería A4J (AJAX for JSF) de RichFaces que sirve para especificar la parte del árbol de componentes JSF que debe ser procesada en el servidor.

Por defecto, si no se define ninguna región (es decir, si no se emplea ) se procesará toda la vista en el servidor. Así pues, es importante usar esta etiqueta si no se desea que se procese toda la página.

Por otra parte, si hay algún componente que no toma el valor introducido tras realizar una petición AJAX, deberemos incluirlo en la región que ha realizado la petición.

Java - Creación de cachés

La siguiente clase define la estructura de una caché de elementos de tipo genérico. Puede resultar muy útil cuando, por ejemplo, debemos mostrar una lista de valores obtenida de un sistema externo: base de datos, servicio web, etc.

La primera vez que se llame al método 'getItems()' para obtener el listado de elementos se realizará una llamada al método 'getNewItems()' que será el encargado de obtenerlos. Este método es abstracto para mayor libertad, ya que permite que el desarrollador realice tantas implementaciones del método como sistemas y/o mecanismos de recogida de datos desee utilizar.

Como se menciona en el javadoc del método 'getItems()', la lista de elementos de la caché se actualizará si dicha lista está vacía o si ha transcurrido más de un día desde la última actualización.


/**
* Generic cache is updated dayly. If there's any problem while trying to
* update the cache, the stored data is kept in the cache and the last modification
* time is updated.
*
* @param Type of the stored element
**/
public abstract class GenericCache {

private Calendar
lastModification;
private List itemList;

/**
* Constructor
**/
protected GenericCache() {
lastModification = new GregorianCalendar();
itemList = new ArrayList();
}

/**
* Checks if cache is empty
* @return true, if cache is empty; false, otherwise
**/
private boolean isEmpty() {
return (itemList == null || (itemList.size() == 0));
}

/**
* Gets the items to be inserted in cache.
*
* @return item list
* @throws Exception if the operation fails when getting items
**/
protected abstract List getNewItems() throws Exception;


/**
* Gets the latest item list from cache. If the cache is empty or if it's
* one day old, there will be an attempt to update it.
*
* @return item list from cache
**/
public List getItems() {
Calendar currentDate = new GregorianCalendar();
try {
if (isEmpty() ||
(lastModification.get(Calendar.DATY_OF_YEAR) != currentDate.get(Calendar.DAY_OF_YEAR))) {
List items = getNewItems();
if ((items != null) && (items.size() > 0)) {
itemList = items;
lastModification = currentDate;
}
}
} catch (Exception e) {
lastModification = currentDate;
}
return itemList;
}



Una posible implementación de esta caché abstracta sería la que se muestra a continuación:

public final class CompanyNamesCache extends GenericCache {

private static CompanyNames c
ompanyNames;
private LocalService localService;

/**
* Constructor
**/
private CompanyNamesCache() {
super();
try {
localService = (LocalService) ServiceLocator.getInstance().getLocalService("java:comp/env/ejb/LocalService");
} catch (NamingException ne) {
localService = null;
}
}

/**
* Gets an instance of the company names cache
*
* @return cache instance
**/
public static synchronized CompanyNamesCache getInstance() {
if (
companyNames == null) {
companyNames = new CompanyNamesCache();
}
return companyNames;
}

/**
* Gets new company names
*
* @return new company names list
* @throw Exception if an error occurs when getting company names
**/
@Override
protected List getNewItems() throws Exception {
List localService.getCompanyNames();
}

}

SQL - Consulta para obtener la información de una tabla

La tabla 'user_tab_columns' describe las columnas de las tablas, vistas y clusters del usuario actual.

Con la siguiente consulta podemos ver la información básica de los campos de una tabla del esquema al que estamos conectados:

SELECT usc.column_id, usc.column_name, usc.data_type, usc.data_length, usc.nullable
FROM user_tab_columns usc
WHERE usc.table_name = 'NOMBRE_TABLA'
ORDER BY usc.column_id;

SQL - Obtener ID de registros cuyo campo XXX esté repetido

Esta consulta devuelve los identificadores (ID) de los registros cuyo campo XXX aparezca en más de una ocasión en la tabla:

SELECT DISTINCT(T1.ID) FROM TABLA T1
WHERE
(SELECT COUNT(*) FROM TABLA T2 WHERE T1.XXX = T2.XXX) > 1


Obviamente, podemos obtener toda la información que nos interese de esos registros e incluir condiciones adicionales en la búsqueda.

Al incluir DISTINCT estamos especificando que la consulta devuelva los identificadores que satisfacen la condición una sola vez, es decir, que no haya IDs repetidos en la salida.

sábado, 25 de abril de 2009

Java - Evitando NullPointerException al comparar strings


Queremos comparar una variable de tipo String con una cadena de caracteres conocida, ¿cómo hacerlo?

a) if (varStr.equals("cadena conocida")) { ...
Esta condición provocará una excepción si 'varStr = null'

b) if (varStr != null && varStr.equals("cadena conocida")) { ...
Ésta otra evita el problema de la anterior.


c) if ("cadena conocida".equals(varStr)) { ...
Mucho más elegante y sencilla. Si no tenemos que analizar el caso en el que la variable pueda ser null, ésta es, sin duda, la mejor opción.