viernes, 2 de diciembre de 2011

JQuery: Notas

Voy a mantener esta entrada activa como cuaderno de notas de JQuery.

Check Boxes

Saber si un checkbox está chequeado:
var isChecked = $('#myCheckBox').attr('checked');

Chequear/deschequear:
$('#myCheckBox').attr('checked', true|false);


Validaciones de formularios

Determinar si un campo de texto está vacío:

if($('#input_text').val().length == 0) {
// Empty field.
}

miércoles, 30 de noviembre de 2011

WordPress: Tema Sliding Doors

Buscando un tema de WordPress para un sitio que estoy montando me topé con "Sliding Doors" ("Puertas Corredizas"), y me agradó bastante por su efecto en el menu principal que hace honor al título del tema.

Como tuve que pasar un rato leyendo el foro para averiguar como personalizar el menú, dejo a disposición una pequeña guía de cómo hacerlo para todo aquel que quiera evitar rascarse la cabeza un rato.

Instalar el tema es bastante sencillo con WordPress. Solo se necesita hacer una búsqueda usando "Sliding Doors". Seleccionamos el tema y lo instalamos.


Vamos añadir una nueva página que va a ser enlazada en el menú deslizante. Usamos la opción de añadir una nueva página.



Para cambiar la imagen que se usará en el menú para esta página damos clic al link de "Imagen destacada" y seleccionamos la imagen de nuestra preferencia.


Seguidamente seleccionamos la opción de "Usar como imagen destacada".

Guardamos los cambios y publicamos la página.


Ahora nos vamos a la sección de menús. Creamos un nuevo menú, le agregamos nuestra página recien creada y lo seleccionamos como el menú de "Sliding Navigation".


Listo, tenemos nuestra página en el menu deslizante.

jueves, 17 de noviembre de 2011

Búsqueda de letras faltantes del abecedario en una oración


Creo que siempre es bueno salvar cualquier código que uno haya hecho por más simple o trivial que parezca. Puede ser que en el momento menos esperado lo requiramos y realmente es frustrante acordarse de que ya uno lo había hecho y luego desechado a la basura.

Dejo para el historial un pequeño código que hice como parte del proceso para aplicar a una empresa. El planteamiento es simple. Codificar un programa que busque las letras faltantes del abecedario en una oración.

import java.util.HashSet;
import java.util.Set;

/**
* Test code to find missing letters of the alphabet from a String sentence.
* @author gabriel.solano
*
*/
public class MissingLetters {

private final int ASCII_CODE_FOR_LETTER_A = 97;
private final int ASCII_CODE_FOR_LETTER_Z = 122;

/**
* Gets the missing letters of a sentence in lower case.
* @param sentence
* @return String having all the letters that the sentence is missing from the alphabet.
*/
public String getMissingLetters(String sentence){
/*
* 1. Let's populate a set with the unique characters of the sentence.
* This approach avoids having two nested for's in the code (better performance).
*/
Set<Integer> uniqueASCIICodes = new HashSet<Integer>();

for (char character : sentence.toLowerCase().toCharArray() ) {
if (character >= ASCII_CODE_FOR_LETTER_A
&& character <= ASCII_CODE_FOR_LETTER_Z) { // Range of lower case letters.
uniqueASCIICodes.add((int)character);

if (uniqueASCIICodes.size() == 26) {
break; // Sentence already covered all letter from the alphabet.
}
}
}
/*
* 2. Move in the range of ascii codes of lower case alphabet
* and check if letter was present in sentence.
*/
StringBuilder misingLettersBuilder = new StringBuilder();

for (int i=ASCII_CODE_FOR_LETTER_A; i <= ASCII_CODE_FOR_LETTER_Z; i++) {
if (!uniqueASCIICodes.contains(i)) {
misingLettersBuilder.append((char)i);
}
}
return misingLettersBuilder.toString();
}

public static void main(String[] args) {

String case1 = "A quick brown fox jumps over the lazy dog";
String case2 = "bjkmqz";
String case3 = "cfjkpquvwxz";
String case4 = "";

MissingLetters missingLetters = new MissingLetters();

System.out.println("Missing letters for[" + case1 + "]: " +
missingLetters.getMissingLetters(case1));
System.out.println("Missing letters for[" + case2 + "]: " +
missingLetters.getMissingLetters(case2));
System.out.println("Missing letters for[" + case3 + "]: " +
missingLetters.getMissingLetters(case3));
System.out.println("Missing letters for[" + case4 + "]: " +
missingLetters.getMissingLetters(case4));
}
}

Salida del programa:

Missing letters for[A quick brown fox jumps over the lazy dog]:
Missing letters for[bjkmqz]: acdefghilnoprstuvwxy
Missing letters for[cfjkpquvwxz]: abdeghilmnorsty
Missing letters for[]: abcdefghijklmnopqrstuvwxyz

martes, 25 de octubre de 2011

Expression Language (EL): Activación


El "expression language" es un tipo de notación en JSP que permite navegar sobre beans de una manera más simplificada y sencilla.

Si tengo por ejemplo un bean "Foo" con una propiedad llamada "prop" y a su vez esta contiene otra propiedad llamada prop2, la forma anticuada de accesar la propiedad sería:

"myFoo.getProp1().getProp2()"

Con EL podemos omitir los "get" de esta forma:

"${myFoo.prop1.prop2}"

Recientemente estaba teniendo un conflicto porque el JSP no me interpretaba el EL. Lo que me estaba faltando era un buena declaración del web.xml que indicara que estamos usando la versión 2.4 de Servlets:

<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

Quería dejar esto documentato porque me tomó rato averiguar como se activaba.

lunes, 24 de octubre de 2011

Sin temor a lucir "fuera de onda"


A veces pareciera que el mundo del desarrollo de software se comporta parecido al mundo de la moda. No lo digo porque piense que esté sujeto completamente a condiciones triviales como la influencia de una estrella pop sobre las jóvenes adolecentes, pero en menor escala, los frameworks estrella más populares tienden a monopolizar la atención de los ingenieros de software con recetas, a veces al punto de pensar que cualquier cosas distinta de la receta está fuera de moda.

Frameworks como Struts, Spring o Hibernate son herramientas exelentes para muchos de los desarrollos de software. El problema considero que comienza cuando un desarrollador cría un esquema en su mente, de que todos los proyectos deben implementarse con la receta estandar "para todas la tallas" que él utiliza. Si alguien más sugiere hacer algo diferente, o solamente menciona en alguna conversación amistosa a otro colega que está utilizando una técnica diferente, el chico receta framework podría verlo como el fuera de onda o inclusive hasta molestarlo por ser un dinosaurio a su parecer.

He estado viendo las exposiciones de la reciente conferencia de Java Zone 2011. Dos presentaciones en particular llamaron mi atención por su valentía para cuestionar el “status-quo”. Una expone un acercamiento diferente a la inyección de dependencias:


Dependency injection when you only have one dependency from JavaZone on Vimeo.

Y la otra, particularmente la que disfruté más, es la de esta entusiasta ingeniera, que con buenos argumentos, se para firme ante su negativa de utilizar Hibernate en sus desarrollos con persitencia en bases de datos.


Hibernate should be to programmers what cake mixes are to bakers: beneath their dignity. from JavaZone on Vimeo.

No estoy tomando partido en ninguna de estas dos presentaciones. Debo confesar que me falta experiencia como para tomar una posición informada en los tópicos mencionados, pero realmente admiro a estos dos por mostrar sin ningún titubeo, dos formas alternativas a técnicas actuales del desarrollo de software. La innovación aparece frequentemente en situaciones donde alguien se aparta de los demás.

Creo que como latinoamericanos tenemos mucho que aprender de estos dos ejemplos. Estamos muy acostumbrados a ser consumidores de tecnologías pero no a ser productores de ellas. Debemos tener la mente abierta para experimentar con nuevas alternativas, utilizando un criterio racional y mucha humildad para examinarlo todo, y ejercitar el jucio informático, el cual es clave en nuestro desarrollo profesional.

martes, 20 de septiembre de 2011

Servlet para descargar archivos

Dejo a disposición como utilizar un servlet para descargar archivos.
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import javax.activation.MimetypesFileTypeMap;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* A simple Servlet to download a file.
* @author gabriel.solano
*
*/
public class DownloadServlet extends HttpServlet{

private static final long serialVersionUID = 4440011247408877539L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
downloadFile(resp,
this.getServletContext().getRealPath("/WEB-INF/myFile.pdf"));
}

protected void downloadFile(HttpServletResponse response, String filePath)
throws ServletException, IOException {

File fileToDownload = new File(filePath);
FileInputStream fileInputStream = new FileInputStream(fileToDownload);

ServletOutputStream out = response.getOutputStream();
String mimeType = new MimetypesFileTypeMap().getContentType(filePath);

response.setContentType(mimeType);
response.setContentLength(fileInputStream.available());
response.setHeader( "Content-Disposition", "attachment; filename=\""
+ fileToDownload.getName() + "\"" );

int c;
while((c=fileInputStream.read()) != -1){
out.write(c);
}
out.flush();
out.close();
fileInputStream.close();
}
}

martes, 6 de septiembre de 2011

Parser de HTML con Java

Quería compartir un código de ejemplo mostrando como "analizar gramaticalmente" (esa fue la traducción que me dio Google Translate para el verbo "parse", de ahora en adelante me quedo con el incorrectamente-usado-pero-mejormente-conocido-verbo: "parsear") una página HTML utilizando la librería de código abierto: HTML Parser. Me ha tocado utilizar esta librería en el pasado para un proyecto en donde necesitábamos extraer todos los enlaces de un sitio, y ahora me tocar usarla para otro proyecto donde necesito validar unas reglas de código en ciertas páginas HTML.
Así que vamos a asumir que necesitamos encontrar todos los enlaces absolutos de una página de un sitio. Primeramente creamos nuestra clase parseadora:
import java.io.IOException;
import java.net.URL;
import org.htmlparser.Node;
import org.htmlparser.Tag;
import org.htmlparser.lexer.Lexer;
import org.htmlparser.util.ParserException;

/**
* Parses the HTML code of the page specified by it's URL.
* @author gabriel.solano
*
*/
public class URLHTMLParser {

/*
* Tag handler that will be used to process the tags.
* (This could be improved by implementing an observer
* pattern to be able to add more than one TagHandler)
*/
private TagHandler tagHandler;

/**
* Constructor.
* @param tagHandler
*/
public URLHTMLParser(TagHandler tagHandler) {
this.tagHandler = tagHandler;
}

/**
* Scans the specified URL.
* @param url
* @throws ParserException
* @throws IOException
*/
public void scanURL(URL url) throws ParserException, IOException {
Lexer lexer = new Lexer(url.openConnection());
extractHTMLNodes(lexer);
}

/**
* Extracts the HTML nodes and lets the TagHandler to do something
* with the tags.
* @param lexer
* @throws ParserException
*/
private void extractHTMLNodes(Lexer lexer) throws ParserException {
Node node;

while (null != (node = lexer.nextNode(false))) {
if (node instanceof Tag) {
Tag tag = (Tag) node;
tagHandler.handleTag(tag);
}
}
}
}


Como podrán notar, la última función de esta clase es la encargada de moverse a través de todos los nodos HTML de la página. Simplemente dejo a la clase "TagHandler" hacer lo que tenga que hacer con la etiqueta que encuentra.
Esta es la interface TagHandler:

import org.htmlparser.Tag;

/**
* Defines the interface for a TagHandler.
* @author gabriel.solano
*
*/
public interface TagHandler {

/**
* Handles the process of an HTML tag.
* @param tag
*/
public void handleTag(Tag tag);

}



Y aquí está la implementación para procesar las etiquetas de ancla:

import java.util.HashSet;
import java.util.Set;
import org.htmlparser.Tag;

/**
* Handles the event when an anchor tag is found while parsing
* HTML code of a page.
* This class has a functionality to count all absolute URLs
* found in the parsing process.
* @author gabriel.solano
*
*/
public class AnchorTagHandler implements TagHandler{

private Set<String> absoluteURLs; // All URLs found.

/**
* Constructor.
*/
public AnchorTagHandler() {
absoluteURLs = new HashSet<String> ();
}

/**
* Gets the found absolute URLs.
* The collection is filled only during the scanning process
* of an HTML page.
* @return
*/
public Set<String>getAbsoluteURLs() {
return absoluteURLs;
}

/**
* Handles the tag only if it is an anchor tag.
*/
public void handleTag(Tag tag) {
if (tag.getTagName().equalsIgnoreCase("a")) {
// Process only if it's an anchor tag.
processTag(tag);
}
}

/**
* Processes the anchor tag. In this case
* adds all absolute URL's found.
* @param tag
*/
private void processTag(Tag tag) {
String href = tag.getAttribute("href");

if (href != null) {
href = href.toLowerCase();
if (href.startsWith("http://") || href.startsWith("https://")) {
// Add all URLs with HTTP protocol.
absoluteURLs.add(href);
}
}
}
}


La función "processTag" simplemente extrae el atributo "href" y verifica si es un URL absoluto. Finalmente creamos la clase principal para correr este código:
import java.net.URL;
import java.util.Set;

public class FindAbsoluteURLs {

public static void main(String[] args) {

AnchorTagHandler anchorTagHandler = new AnchorTagHandler();
URLHTMLParser htmlParser = new URLHTMLParser(anchorTagHandler);

try {
htmlParser.scanURL(new URL("http://www.crjug.org/"));
Set<String>urls = anchorTagHandler.getAbsoluteURLs();

for(String url : urls) {
System.out.println(url);
}

} catch (Exception e) {
e.printStackTrace();
}
}
}

Usando el sitio de la comunidad de usuarios de Java de Costa Rica obtenemos los siguientes URLs:

http://www.facebook.com/pages/costa-rica-jug/107081646760
http://www.oreilly.com/
http://www.java.net/

Esta es la dependencia de Maven en caso necesiten utilizar esta útil librería:
<dependency>
<groupId>org.htmlparser</groupId>
<artifactId>htmlparser</artifactId>
<version>1.6</version>
</dependency>

jueves, 25 de agosto de 2011

PHP Cache

Una estrategia de cache es siempre una de las primeras opciones a tomar cuando se requiere mejorar el desempeño de una aplicación. Recientemente tuve que buscar una opción disponible para una aplicación web que estabamos trabajando en PHP, y de las que consulté por Internet, me quedé con la que ofrece este amigo en este artículo.

Esta clase PHPCache utiliza una tabla en base de datos con tres columnas como mecanismo de almacenamiento del cache:
  • PHPCache_key: la llave del objeto a guardar
  • PHPCache_value: el valor a guardar
  • PHPCache_expires: cuando expira el objeto en cache
Nuestro amigo ofrece un código que ejemplifica muy bien el uso de la clase:

require_once 'phpcache.class.php'; // Se incluye la clase de PHPCache.

// Parametros de conexión de la Base de datos.
$database = array(
'type' => 'mysql',
'name' => 'YOUR DATABASE NAME',
'table' => 'PHPCache',
'user' => 'YOUR DATABASE USERNAME',
'pass' => 'YOUR DATABASE USERNAME\'S PASSWORD',
'host' => 'localhost' /* Or maybe IP address of your database server */
);

// Llamado de configuración de la base de datos.
PHPCache::configure($database);
// Se obtiene una única instancia (patrón Singleton).
$cache = PHPCache::instance();

// Se pregunta primero si los resultados ya están en cache.
if( ($results = $cache->get('result_of_some_nasty_code')) !== false ) {
$tpl->assign($results);
/* Or maybe return $results or whatever depending on where you use this */
} else {
// Los datos todavía no estan en cache. Hay que obtenerlos de la manera lenta.
/***********************
* Your slow code here
***********************/
// Inmediatamente se guardan en cache. La siguiente vez no se hará de la forma lenta.
$cache->store('result_of_some_nasty_code', $results_of_your_slow_code, PHPCACHE_1_HOUR * 5); /* Cache for 5 hours */
}

viernes, 19 de agosto de 2011

Struts 1: Descargando la excepción en un DownloadAction



En un proyecto para el cliente requeríamos descargar un PDF que se generaba de manera dinámica. Así que decidimos usar el DownloadAction de Struts 1 que está disponible en un paquete llamado "struts-extras". Por ciertos inconvenientes en el proyecto la aplicación fallaba en el ambiente del cliente y teníamos que estar visitando los logs para ver las excepciones. Dependiendo de la configuración de un servidor, buzear en logs puede ser una tarea sumamente laboriosa lo cual fue en mi caso particular.

Así que para facilitar el análisis decidí agregar un pedazo de código que cuando atajara la excepción, esta misma se pudiera descargar como un ".txt". Utilizé una función que me encontré en Internet que convierte el StackTrace a un String. Después el String se convierte a un arreglo de Bytes que se descarga finalmente como un TXT. Dejo el código a disposición.


import java.io.ByteArrayInputStream;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.actions.DownloadAction;

public class OutputPDFAction extends DownloadAction {

@Override
protected StreamInfo getStreamInfo(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws Exception {

try{
// Code might throw an exception.

response.setHeader("Content-disposition", "attachment; filename=patient_diary.pdf");
String contentType = "application/x-download";

return new ByteDownloadStreamInfo(contentType,abyte0);

}
catch(Throwable throwable)
{
response.setHeader("Content-disposition", "attachment; filename=error.txt");
String contentType = "application/x-download";

return new ByteDownloadStreamInfo(contentType,
stackTraceToString(throwable).getBytes());
}
}

private String stackTraceToString(Throwable e) {
String retValue = null;
StringWriter sw = null;
PrintWriter pw = null;
try {
sw = new StringWriter();
pw = new PrintWriter(sw);
e.printStackTrace(pw);
retValue = sw.toString();
} finally {
try {
if(pw != null) pw.close();
if(sw != null) sw.close();
} catch (IOException ignore) {}
}
return retValue;
}


class ByteDownloadStreamInfo implements StreamInfo {

private byte[] bytes;
private String contentType;

public ByteDownloadStreamInfo(String contentType, byte[] bytes) {

this.bytes = bytes;
this.contentType=contentType;
}

public String getContentType() {
return this.contentType;
}

public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(this.bytes);
}
}
}


miércoles, 10 de agosto de 2011

PHP: Cálculo de semana laboral actual

Dejo a disposición un código que permite obtener la semana laboral actual (lunes a viernes tomando domingo como inicio de semana).

Salida del código:

From: 08-08-2011 To: 08-12-2011


<?php


class DateUtils {

public static function get_current_date() {
return substr(DateUtils::get_current_date_time(), 0, 10);
}

public static function get_current_date_time() {
return date('Y-m-d H:i:s');
}

public static function get_day_of_current_week_as_number() {
return date("w",mktime());
}

public static function get_day_of_week_as_number($date_as_string) {
return date("w", strtotime($date_as_string));
}

public static function substract_days_to_current_date($days) {
$operation = "-";

if ($days < 0) {
$operation = "+";
$days = abs($days);
}
$newdate = strtotime ( $operation. $days .' day' ,strtotime ( date('Y-m-d') )) ;
return date ( 'm-d-Y' , $newdate );
}

public static function substract_days_to_date($date_as_string, $days) {

$operation = "-";

if ($days < 0) {
$operation = "+";
$days = abs($days);
}
$newdate = strtotime ( $operation . $days .' day' , strtotime ( $date_as_string) ) ;
$newdate = date ( 'm-d-Y' , $newdate );
return $newdate;
}

public static function get_start_day_of_current_work_week() {
$monday_delta = 0;
$current_day_of_week = DateUtils::get_day_of_current_week_as_number();

if($current_day_of_week > 1) {
$monday_delta = $current_day_of_week-1;
} else if($current_day_of_week == 0){
$monday_delta = -1;
}
return DateUtils::substract_days_to_current_date($monday_delta);
}

public static function get_start_day_of_work_week($date_as_string) {
$monday_delta = 0;
$day_of_week = DateUtils::get_day_of_week_as_number($date_as_string);

if($day_of_week > 1) {
$monday_delta = $day_of_week-1;
} else if($day_of_week == 0){
$monday_delta = -1;
}
return DateUtils::substract_days_to_date($date_as_string, $monday_delta);
}

public static function get_delta_for_end_day_of_work_week($current_day_of_week) {
$friday_delta = 0;

switch ($current_day_of_week) {
case 0: // Sunday.
$friday_delta=-5;
break;
case 1: // Monday.
$friday_delta=-4;
break;
case 2: // Tuesday.
$friday_delta=-3;
break;
case 3: // Wednesday.
$friday_delta=-2;
break;
case 4: // Thursday.
$friday_delta=-1;
break;
case 5: // Friday.
$friday_delta=0;
break;
case 6: // Saturday.
$friday_delta=1;
break;
}
return $friday_delta;
}

public static function get_end_day_of_current_work_week() {
$friday_delta = 0;
$current_day_of_week = DateUtils::get_day_of_current_week_as_number();

return DateUtils::
substract_days_to_current_date(DateUtils::
get_delta_for_end_day_of_work_week($current_day_of_week ));
}

public static function get_end_day_of_work_week($date_as_string) {
$friday_delta = 0;
$current_day_of_week = DateUtils::get_day_of_week_as_number($date_as_string);

return DateUtils::
substract_days_to_date($date_as_string, DateUtils::
get_delta_for_end_day_of_work_week($current_day_of_week ));
}

public static function get_current_work_week_range() {
return "From: " . DateUtils::get_start_day_of_current_work_week() . " To: "
. DateUtils::get_end_day_of_current_work_week();
}

}
echo "<h1>" . DateUtils::get_current_work_week_range() . "</h1>";
?>


martes, 9 de agosto de 2011

Eclipse: Configurar dos carpetas fuentes con un mismo destino de deploy


Hace poco tuve que correr para acomodar un proyecto que el cliente pedía con cierta estructura de carpetas. Aparentemente el cliente tiene como norma separar en dos carpetas el código de la aplicación. En una pone todo el código que corre del lado del cliente: “htdocs”, y en otra carpeta todo el código que corre del lado del servidor (JSP’s, Structs actions, etc): “Java”.

Lo interesante es que los JSP’s igual tenían que llamar códigos de CSS y JavaScript que están situados en la carpeta de htdocs la cual está fuera del directorio raíz de la aplicación Java. Me puso a pensar un buen rato hasta que caí en razón que al final el cliente hace un deploy de las dos carpetas en una misma. Así que descubrí como hacer esto con Eclipse. Igualmente se puede hacer con cualquier herramienta de construcción como ANT o Maven, pero para los fines propios la opción que muestro a continuación fue suficiente.

Se hace clic derecho sobre el proyecto web, se va a propiedades y en la opción de “Deployment Assembly”, simplemente se agregan las dos carpetas fuentes con un mismo path de deploy. Así de sencillo.



viernes, 8 de julio de 2011

JavaScript: Creando un formulario de manera dinámica

Este es un código simple de JavaScript incrustado en el HTML, que crea un formulario (form) dinámicamente, para luego ser enviado de manera inmediata por POST enviando una variable (<input type="hidden">).


<html>
<body>
<script type="text/javascript">
var form=document.createElement("form");
form.setAttribute("action","target.php");
form.setAttribute("method","get");
var input=document.createElement("input");
input.setAttribute("value","x12kfh378");
input.setAttribute("type","hidden");
form.appendChild(input);
document.body.appendChild(form);
form.submit();
</script>
</body>
</html>

lunes, 4 de julio de 2011

Struts 2: Configurar tamaño máximo para subida de archivos

Para configurar el tamaño máximo de subida de archivs ("uploading") en Struts 2, simplemente se agrega la siguiente constante en el struts.xml remplazando por el valor deseado:

<struts>
<constant name="struts.multipart.maxSize" value="50000000" />
....
</struts>

Esto evita la excepción cuando se sobrepasa el límite por defecto.

"the request was rejected because its size exceeds the configured maximum"

viernes, 24 de junio de 2011

Eclipse IAM: Instalación para Eclipse Helios

Hay dos formas generales de usar Maven:
  • "A lo macho" a punta de comandos.
  • O a lo no tan macho usando un plugin de Maven.
Si no se quiere gastar neuronas recordando comandos de Maven, entonces la solución óptima es usar un itermediador, que en mi caso particular el que utilizo es un plugin de Maven para Eclipse llamado IAM (Integración de Apache Maven).

Es muy sencillo de instalar en la versión Helios de Eclipse:

1. Copiar el URL de actualización del sitio:
http://q4e.googlecode.com/svn/trunk/updatesite-iam/


2. Ir a la opción de menú: "Ayuda->Instalar Nuevo Software"


3. Pegar el URL en la opción de "Agregar Sitio". Dar clic a "Siguiente"


4. Seleccionar los paquetes deseados. Preferiblemente elegirlos todos para menos complicación.




5. Después de reiniciar el eclipse probar el plugin creando un nuevo proyecto Maven.




6. Seleccionar el checkbox de crear un proyecto simple de Java.



7. Si examinamos el archivo pom.xml observamos que se agregó una dependencia de JUnit.




8. Podemos probar la integración de la dependencia al proyecto corriendo la clase de prueba que se generó de ejemplo: "AppTest".




viernes, 17 de junio de 2011

PHP: Leer un XML usando DOMDocument

Dejo a disposición un ejemplo sencillo de como leer un XML con la clase DOMDocument.

XML:

<catalog>
<catalog-name>My Favorite Music</catalog-name>
<cd id="cd-001">
<title value="Empire Burlesque" />
<artist value="Bob Dylan" />
<country value="USA" />
<company value="Columbia" />
<price value="10.90" />
<year value="1985" />
</cd>
<cd id="cd-002">
<title value="Hide your heart" />
<artist value="Bonnie Tyler" />
<country value="UK" />
<company value="CBS Records" />
<price value="9.90" />
<year value="1988" />
</cd>
<cd id="cd-003">
<title value="Greatest Hits" />
<artist value="Dolly Parton" />
<country value="USA" />
<company value="RCA" />
<price value="9.90" />
<year value="1982" />
</cd>
</catalog>

Código:

<?php
$cdCatalogXMLReader = new CDCatalogXMLReader();
$cdCatalogXMLReader->showCatalogAsTable('cd-catalog.xml');

class CDCatalogXMLReader {
public function showCatalogAsTable($xmlPath) {
// Loads XML.
$doc = new DOMDocument();
$doc->load($xmlPath);

// Reading tag's value.
$title = $doc->getElementsByTagName("catalog-name")
->item(0)->nodeValue;

echo "<h1>$title</h1>";

// Reading all elements with tag name="cd".
$cds = $doc->getElementsByTagName( "cd" );
echo '<table border="1">';
echo '<tr><th>ID</th><th>Title</th><th>Artist</th><th>Country</th><th>Company</th><th>Price</th><th>Year</th></tr>';

foreach ($cds as $cd) {
echo '<tr>';
// Reading attributes.
echo '<td>' . $cd->getAttribute('id') . '</td>';
echo '<td>' . $cd->getElementsByTagName("title")->item(0)->getAttribute('value') . '</td>';
echo '<td>' . $cd->getElementsByTagName("artist")->item(0)->getAttribute('value') . '</td>';
echo '<td>' . $cd->getElementsByTagName("country")->item(0)->getAttribute('value') . '</td>';
echo '<td>' . $cd->getElementsByTagName("company")->item(0)->getAttribute('value') . '</td>';
echo '<td>' . $cd->getElementsByTagName("price")->item(0)->getAttribute('value') . '</td>';
echo '<td>' . $cd->getElementsByTagName("year")->item(0)->getAttribute('value') . '</td>';
echo '</tr>';
}
echo '</table>';
}
}
?>

Salida:

My Favorite Music

IDTitleArtistCountryCompanyPriceYear
cd-001Empire BurlesqueBob DylanUSAColumbia10.901985
cd-002Hide your heartBonnie TylerUKCBS Records9.901988
cd-003Greatest HitsDolly PartonUSARCA9.901982

lunes, 13 de junio de 2011

Patrón de diseño: Prototype


Estuve estudiando este patrón de diseño para enseñarlo en una clase. Este patrón en resumen permite crear nuevos objetos al hacer una copia exacta de un objeto prototípico.

El principal uso que le encuentro al patrón, al menos de primera impresión ya que los patrones se van digiriendo con el tiempo, es el ahorro que se logra en casos donde la instanciación de un objeto es costoso en términos de recursos del sistema (memoria, tiempo, procesamiento, etc).

En el ejemplo de abajo simulo esta aplicación. La clase Sheep (oveja) cuando se instancia require la construcción de su cadena de ADN; este es el supuesto proceso pesado. Java ya incluye una interface llamada "Cloneable" que cuando se implementa en una clase, las instancias de la clase se pueden clonar. Cuando hablamos de clonar nos referimos a crear una copia exacta de un objeto al dejar los mismos valores de sus atributos de clases en una instancia aparte.

La clase Sheep implementa por tanto esta interface con lo cual crear otra oveja similar es asunto simplemente de llamar el método "clone()".

public class Sheep implements Cloneable {

protected String dna;
protected final char [] DNA_ALPHABET = {'A','C','T','G'};

public String getDNA() {
return dna;
}

public Sheep() {
// Simulates extremely long process to create a sheep.
StringBuilder dnaBuilder = new StringBuilder();

for(int i=0; i < 30; i++) {
int alphabetIndex = (int) (Math.random() * 10 % 4);
dnaBuilder.append(DNA_ALPHABET[alphabetIndex]);
}
dna = dnaBuilder.toString();
}

@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}

public static void main(String[] args) {
Sheep dolly = new Sheep();
System.out.println("Dolly was born: " + dolly.getDNA());

try {
Sheep dollyClon = (Sheep) dolly.clone();
System.out.println("Dolly Clon: " + dollyClon.getDNA());

} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}

Resultado:

Dolly was born: CAAACGGGACACTTATACTTGTTCGCCCAT
Dolly Clon: CAAACGGGACACTTATACTTGTTCGCCCAT

viernes, 10 de junio de 2011

Siguiendo la ruta de los redireccionamientos

Hay ocasiones en que me ha tocado utilizar servicios externos donde una dirección llama otra, después se devuelve y al final redirecciona a otro sitio. Como todo eso sucede en centésimas de segundo, es imposible darle seguimiento a que URLs viaga o cuáles son los parámetros que se envían en cada viaje. Firebug no nos ayuda mucho, porque a pesar de que tiene una sección de red donde se puden ver las distintas solicitudes de HTTP, estas se borran en cada redireccionamiento.

Para poder darle seguimiento a los distintos requests, recomiendo cualquiera de las dos siguientes herramientas: HTTPWatch y HTTPFox. La primera funciona en todos los navegadores y es la que yo uso en particular. La segunda es un plugin de Firefox. En los screenshots de abajo ejemplifico como uno puede observar el típico redireccionamiento del sitio de google.com, a la versión respectiva del país de origen de la solicitud.

HTTPWatch


HTTPFox


martes, 24 de mayo de 2011

Wordpress: Fatal error: Allowed memory size of 33554432 bytes exhausted

Estaba probando instalar un plugin para Wordpress en el hosting, y de repente ya no me cargaba el sitio y solo aparecía el siguiente error:

Fatal error: Allowed memory size of 33554432 bytes exhausted

La solución es simple. Se edita el archivo wp-settings.php agregando la siguiente línea para incrementar la memoria (en este caso al doble):

ini_set("memory_limit","64M");

Según leí en algunos foros, a veces la cantidad de tablas en la BD o la complejdiad de ellas, puede causar un incremento en la memoria requerida.

lunes, 16 de mayo de 2011

PHP: Desplegar datos de cualquier tabla MySQL


Si alguna vez necesitan hacer una página rápida de prueba para desplegar los datos de cualquier tabla MySQL, aquí les dejo esté código que encontré en este post. Solamente hay que cambiar los datos de conexión y la tabla que desean desplegar.

<?php

//MyDB represents the name of your database.
//MyTable represents the name of your table inside the database.

mysql_connect("localhost","my_user","my_pass") or die("Unable to connect to SQL server");
@mysql_select_db("my_database") or die("Unable to select database");


$result = mysql_query("select * from MY_TABLE limit 500");
?>
<table border="1">
<tr>
<?php


while ($field=mysql_fetch_field($result)) {
echo "<th>";
echo "$field->name";
echo "</th>";
}
echo "</tr>";

while ($row = mysql_fetch_row($result)) {
echo "<tr>";
for ($i=0; $i<mysql_num_fields($result); $i++) {
echo "<td>";
if (isset($row[$i]))
echo "$row[$i]";
else
echo "-";
echo "</td>";
}
echo "</tr>\n";
}

echo "</table>";

?>

martes, 3 de mayo de 2011

TortoiseSVN: Evitar esa molesta ventana para colocar la contraseña

(In English: "TortoiseSVN: Avoid password prompting")

Este es un truco muy conocido entre los usuarios de esta herramienta. Uno se pregunta porque no agregan una opción para recordar contraseña como es lo más lógico y común.

Lo que uno hace es abrir la configuración de red y en la parte de network se coloca el path del cliente de SSH que es el mismo "TortoisePlink.exe". Se agrega al final los parametros de login:

TortoiseSVN\bin\TortoisePlink.exe" -l gabriel.solano -pw myPsw

lunes, 2 de mayo de 2011

JQuery: Leer un XML y asignarlo a un arreglo asociativo

(In English: "Read XML into an associative array with JQuery")

Dejo a la disposición un pequeño ejemplo para leer un XML y asignar los datos a un arreglo asociativo de JavaScript (en dos dimensiones) usando JQuery.

Este es el XML de ejemplo:
<catalog>
<cd id="cd-001">
<param name="title" value="Empire Burlesque" />
<param name="artist" value="Bob Dylan" />
<param name="country" value="USA" />
<param name="company" value="Columbia" />
<param name="price" value="10.90" />
<param name="year" value="1985" />
</cd>
<cd id="cd-002">
<param name="title" value="Hide your heart" />
<param name="artist" value="Bonnie Tyler" />
<param name="country" value="UK" />
<param name="company" value="CBS Records" />
<param name="price" value="9.90" />
<param name="year" value="1988" />
</cd>
<cd id="cd-003">
<param name="title" value="Greatest Hits" />
<param name="artist" value="Dolly Parton" />
<param name="country" value="USA" />
<param name="company" value="RCA" />
<param name="price" value="9.90" />
<param name="year" value="1982" />
</cd>
</catalog>

Y el código HTML/JQuery:
<html>
<head>
<title>XML into associative array with JQuery</title>
<script type="text/javascript" src="jquery-1.5.2.min.js"></script>
<script type="text/javascript">
var cdArray = new Array(); // Will hold the XML data.

// Request to XML.
$.ajax({
type: "GET",
url: "cd-catalog.xml",
dataType: "xml",
async: false,
success: readXMLIntoArray
});

/**
Move XML data into associative array.
*/
function readXMLIntoArray(xml) {
$(xml).find('cd').each(function(){
var id = $(this).attr('id');
var parametersArray = new Array();
$(this).find('param').each(function(){

var name = $(this).attr('name');
var value = $(this).attr('value');

parametersArray[name] = value;
});
cdArray[id] = parametersArray;
});
}

// Testing array
document.write("<p>Title:" + cdArray["cd-002"]["title"] + "</p>");
document.write("<p>Artist:" + cdArray["cd-002"]["artist"] + "</p>");

</script>
</head>
<body>
</body>
</html>

Salida:

Title:Hide your heart

Artist:Bonnie Tyler

miércoles, 27 de abril de 2011

Conversión de formato por lote de imágenes con IrfanView


Hoy tuve la oportunidad de recordar una vieja herramienta que utilizaba en mis años universitarios; El IrfanView. Esta herramienta destacaba en aquel entonces por ser bien ligera y rápida para navegar en galería de imágenes. Hoy volvió a resurgir porque andaba buscando una herramienta que me permitiera hacer conversión de archivos por lote. Convertir una carpeta con una cantidad grande de imágenes BMP a JPG. En inglés a este tipo de operación se le llama "batch conversion". No defraudó el IrfanView haciendo la operación de manera eficiente.


Links de descarga:

martes, 26 de abril de 2011

Cacoo: Excelente opción en línea para creación de diagramas

Me puse a investigar hoy acerca de alguna herramienta que me permitiera crear wireframes para un sitio web que tengo en mente. En un par de minutos aterricé en un artículo donde se recomiendan las mejores 15 herramientas para wireframes entre las cuales hay varias gratuitas y otras de pago.

Sorprendentemente, el primer lugar es una herramiente gratuita en línea que después de probarla un poco, me di cuenta que no iba a necesitar explorar el resto de la lista. Esta herramienta se llama Cacoo y es en verdad gratuita, no como las herramientas que te permiten crear lo que querías para luego darte cuenta que no puedes salvar o exportar tu trabajo, o te meten leyendas en agua.

Cacoo se puede utilizar para todo tipo de diagramas, y asombra su facilidad de uso y diversas opciones que ofrece. En particular me gusta las guías de layout que muestra cuando uno va a mover un elemento. Cuando lo probé con el wireframe, me muestra la alineación de un elemento con respecto a las cajas del menú horizontal. Eso es de bastante ayuda para dejar todo bien ordenado.

Este es un ejemplo bastante sencillo. Afortunadamente el sitio ofrece una plantilla de wireframe que uno puede usar como referencia de todo lo que se puede hacer.


Si fuera algún tipo de gerente de nuevas adquisiones de Google, fijo pondría esta herramienta junto con la compañía en la mira.

sábado, 23 de abril de 2011

Programación Android: Primeros pasos

En esta entrada de blog quisiera recomendar un video tutorial que explica de manera muy concisa, cómo instalar el ambiente para el desarrollo en Android. No pretendo detallar mucho pues el tutorial es muy claro. En resumen, los pasos a seguir son los siguientes:
  1. Revisar pre-requisitios: Java SDK (1.6 hacia arriba preferiblemente) y Eclipse como IDE de desarrollo (cualquier versión reciente funciona. En mi caso yo utilizo Helios).
  2. Decargar e instalar el SDK de Android.
  3. Ejecutar el administrador del SDK de Android (SDK manager).
  4. Ir al menú de la izquierda en la opción de paquetes disponibles ("available packages") y seleccionar la versión del API para descargarla. En este momento la 2.2 es la más utilizada. También es bueno descargar los ejemplos de la API.
  5. Instalar el plugin Android para eclipse (instrucciones disponibles en el sitio del SDK).
  6. Ir a las preferencias de Eclipse para configurar el path en donde está el SDK de Android.
  7. Para probar que todo funciona bien, creamos un nuevo proyecto de tipo Android el cual crea una clase principal con un simple "Hola Mundo".
  8. Ejecutamos el proyecto como una aplicación de Android.
  9. La primera vez para a solicitar que se configure en un emulador de un dispositivo Android. Se hace con las características deseadas como tamaño de pantalla y cantidad de memoria del dispositivo.
  10. Arranca el emulador (puede tomar un rato) y se ejecuta la aplicación desplegando el hola mundo.

miércoles, 20 de abril de 2011

Notepad++ : Plugin "Compare" para sacar diferencias entre archivos

Necesitaba hacer una comparación entre dos versiones de un archivo así que me di a la tarea de buscar una típica herramiente de comparación; también a veces conocidas como DIFF. Después de buscar un poco pensé que uno de los mejores editores de texto, Notepad++, debería tener un funcionalidad de comparación ya incluída.

Efectivamente, en las más recientes versiones de este editor ya viene un plugin incluido: "Compare".


Así que otro Kudos a esta magnífica herramienta.

lunes, 11 de abril de 2011

PHP: Configurando bien el path para tus galletas

Recientemente me di cuenta que el parámetro ´path ´ es necesario a la hora de configurar una galleta (cookie en inglés) si se quiere que otra página en un path diferente (no en un subdominio diferente) pueda accesarla. Me tomó rato descubrir esta verdad elemental de las galletas.

Si se desea que cualquier path del mismo subdominio pueda leer la galleta, entonces el path se pone con la típica barra inclinada que representa el directorio raíz.

setcookie("cookieName", "cookieValue", time() * 3600 * 24 * 30 /*30 days*/, "/");

miércoles, 6 de abril de 2011

Diccionario Java-PHP

Dado que recientemente he tenido que meterme a codificar un poco en PHP, he decidido mantener una entrada de post para ir traduciendo sentencias de lenguaje Java a PHP. Esto dado que se puede decir que Java es mi "lenguaje nativo". Me resulta más fácil imaginarme como lo haría en Java y luego simplemente buscar su traducción a PHP.


Pare revisar que una cadena no es nula y no viene vacía

Java
if (someString != null && someString != "") { // Do something.}
PHP
if (isset(someString) && ! empty(someString)) { // Do something. }

Pare revisar si una cadena está contenida en otra

Java
if (plainString.contains("foo!")) { // Do something. }
PHP
if (strpos($plainString, "foo!") !== false) { // Do something. }

Reemplazar una secuencia de caracteres por otra

Java
myString = myString.replace("x", "y");

PHP
str_replace("x", "y", myString);



Obtener el tamaño de un arreglo


Java
int size= array.length;

PHP
$size = count($array);

martes, 5 de abril de 2011

Error de instalación en WordPress: "failed to open stream ../wp-load.php"

En estos días me convencí por testimonios personales, de mejor tratar de usar WordPress en lugar de Joomla como CMS para un sitio que tengo en mente. Aparentemente una de las características principales de WordPress es su facilidad de uso. Eso incluye la instalación que en teoría se hace en menos de 5 minutos. Pero por ley de Murphy caí en el peor caso. Seguí los pasos que básicamente pedían solo configurar la información para la conexión de la base de datos. El problema comenzó cuando traté de accessar el "install.php". Me daba este error:

require_once(../wp-load.php) [function.require-once]: failed to open stream: No such file or directory in /un/path/largo/wp-admin/install.php on line ##

Como en todo, después de googlear un buen rato, leí en un foro que a veces el archivo zip se descomprime incorrectamente dejando los paths incorrectos. En mi caso el susodicho "wp-load.php" estaba quedando al mismo nivel del "install.php". Y como se observa la ruta lo pide un nivel más arriba.

Así que volví a extraer el zip seleccionando la opción de extraer rutas absolutas:


Y listo. El resto de la instalación sí fue verdaderamente express. Ahí continuaré informando de mi experiencia con dicho CMS.

lunes, 14 de marzo de 2011

eZ Publish: Habilitando inserción de código HTML

Me ha tocado por necesidades de un nuevo cliente de la empresa, comenzar a empaparme de eZ Publish, un CMS open source que entre otras cosas, permite la publicación de contenido de manera multi-canal. Esto quiere decir que el contenido publicado se puede ver por ejemplo tanto en un navegador común y silvestre, como en un iPhone. En este último el contenido se adapta a las dimensiones del celular.

Lo primero que necesitaba hacer era ver como subir un archivo con bastante contenido HTML y JavaScript. Pero como todo buen CMS, esta opción no está habilitada de manera directa. Supongo que es una restricción ideológica para no dejar fácilmente que el CMS pierda el control del contenido. Me tomó bastante tiempo averiguarlo pero finalmente encontré la respuesta en un foro. Dejo la solución a la disposición.

1. Abrimos el archivo de configuración de eZ Publish: {eZPublis-4.3}/settings/content.ini

2. Vamos a la sección que dice [literal], la cual está de esta manera:

[literal] AvailableClasses[]
# The class 'html' is disabled by default because it gives editors the
# possibility to insert html and javascript code in XML blocks.
# Don't enable the 'html' class unless you really trust all users who has
# privileges to edit objects containing XML blocks.
#AvailableClasses[]=html CustomAttributes[]

y agregamos la etiqueta html en "AvailableClasees":

[literal]
AvailableClasses[]=html


3. Por último abrimos el editor, y preferiblemente deshabilitando el editor "Rich" (o en caso contrario usando el botón de "literal"), agregamos lo siguiente:

<literal class="html">

Aquí va el código HTML...

</literal>

domingo, 6 de marzo de 2011

Tendencias modernas de Recursos Humanos

Dejo a disposición (y discreción) el siguiente ensayo que tuve que hacer como parte del curso de Recuros Humanos que estoy llevando como parte del diplomado en Administración de Empresas. Disculparán si encuentran faltas de ortografía o de redacción. Tuve que sacarlo un toquecín rápido.

<formato PDF>

Introducción


Todos hemos escuchado cierta frase que ya nos cae a veces algo trillada, pero que no se aleja para nada de la realidad, de que estamos viviendo la época de la información. Lo que realmente se ha vuelto valioso es la información. Y en verdad no es ninguna realidad inventada. Hemos presenciado el surgimiento de empresas altamente exitosas que usan como materia prima nada más que la información. Casos ejemplares son los de las compañías Google y Facebook. Ambas empresas tienen muchas cosas en común. Las dos nacieron casi conjuntamente con la web, ambas manejan información global que no publican ellas mismas, las dos supieron hacer la conexión ideal entre tecnologías de información y datos públicos en bruto, etc.

Otro fenómeno que muchos no pudieron anticipar década atrás, es el surgimiento de la filosofía de código abierto, de la cual también se desprende la filosofía de las comunidades abiertas. Y es que quién se podría haber imaginado que en la actualidad tendríamos miles de cientos de personas alrededor del mundo, trabajando en proyectos demandantes que no les deja ninguna retribución de tipo económica. Solo queda la satisfacción personal de contribuir a determinada comunidad sabiendo que han dejado un legado al mundo. Ahora eso nos suena común. Pero si nos ponemos a meditar en ello, desde una perspectiva consumista altamente capitalista, parece extraño que la gente dedique tiempo personal sin obtener dinero por ello. No obstante las empresas tienen que seguir pagándole a las personas por su trabajo; tampoco es que estamos en una utopía futuro-comunista como la que se dibuja en la serie de ciencia ficción StarTrek. Todos todavía necesitamos comer y pagar nuestras cuentas.

Sin embargo, uno se pregunta si en algo pueden las empresas aprovechar esta creciente tendencia global del ser humano de no solo utilizar sus habilidades profesionales en una empresa, en jornada laboral, para ganarse un sueldo y llevar el pan a su casa, para fortalecer las necesidades típicas de recurso humano como reclutamiento, capacitación, evaluación de desempeño, etc . Qué tan rápido se han adaptado las compañías para competir captando y desarrollando el recurso que las mayoría de ellas confiesan, al menos de palabra, que es el recuro más valioso para ellas; el recurso humano.

En este ensayo pretendo hacer un corto análisis en cuanto a la influencia, poca o grande, las conclusiones de este trabajo lo dirán, que ha tenido el enfoque global de la planificación del recurso humano de una empresa a causa de estas dos grandes tendencias modernas: importancia y democratización de la información, y la creciente motivación filantrópica del profesional. Como ya se habrá notado, he comenzado este ensayo citando ejemplos (y lo seguiré haciendo más adelante) de empresas que caen de una u otra forma dentro del área de las tecnologías de la información. Como profesional en ciencias de la computación se puede comprender la pasión intrínseca de estudiar a fondo las empresas tecnológicas más revolucionarias de nuestro tiempo.

El papel de las redes sociales [ Inteligencia de reclutamiento]


Hoy en día muy pocos pueden alegar desconocer qué es una red social. Quizás las generaciones más viejitas llevando un rezago tecnológico se podrían excusar. Aunque ya casi ni eso porque también somos testigos de cómo hasta nuestras abuelitas tienen un perfil en Facebook para seguir y hasta fiscalizar qué hacen sus nietos. ¿Pero tiene este fenómeno nacido de lo que se conoce como la Web 2.0, mayor trascendencia en lo que respecta al ámbito de recursos humanos? La respuesta pareciera ser afirmativa.

“La competencia por conseguir talento es cada día más feroz. ¿Está utilizando las herramientas adecuadas para aumentar tus posibilidades de éxito? Descubra cómo LinkedIn reclutador puede ayudar a su empresa encontrar y contratar a los mejores talentos, dando acceso a toda nuestra red global de más de 80 millones de profesionales. “6

De esta forma se vende una de las redes sociales de profesionales con mayor crecimiento en el globo. Hoy en día vemos como surgen a cada momento redes sociales especializadas. Para colarse en este mercado de la socialización virtual, las empresas emprendedoras saben que es una pérdida de tiempo competir con Facebook. Es por ello que se han dedicado a crear nuevos conceptos de redes. LinkedIn es una red social que se especializa en crear redes sociales de profesionales. Quizás por ejemplo, a una persona no le interese agregar a un jefe a su red social de amigos. No le gusta la idea de que su jefe sepa de su vida personal. Pero sí se siente motivado a agregarlo a su red profesional, más si logra que este le escriba una recomendación en su perfil. Capacidad permitida en la página.

Otras característica interesante de esta red es que uno puede señalar relaciones profesionales entre sus contactos, por ejemplo decir que “Gabriel Solano (Yo) fue compañero de María Pilar”, teniendo María Pilar que confirmar si es cierto. Esto va produciendo que se forme una red mucho más robusta y de la cual se puede sacar información relevante que podría ser de enorme interés para los reclutadores de empresas. Como dice el reconocido refrán: “Dime con quién andas y te diré quién eres”. El departamento de reclutamiento puede realizar una estudio previo de la persona usando su perfil, antes de determinar si realizarle o no una entrevista.

En esta última década hemos presenciado como los viejos sistemas, pero aún relevantes, de anuncios de ofertas en periódicos, han ido mermando como medio principal para atraer el mejor recurso disponible. La idea de mantener una hoja de vida en línea ayuda a estar siempre abierto a nuevas oportunidades sin siquiera estarlas buscando. Las empresas están constantemente consultando bases de datos de múltiples bolsas de empleo para buscar gente especializada que muchas veces es muy difícil de conseguir por los viejos medios.

Todo profesional de la actualidad que quiera maximizar sus posibilidades de ser contratado por una empresa altamente reconocida, debe considerar todas las opciones de bolsas de empleo en línea, y ahora incluyendo también las redes sociales de profesionales.

Google y su apuesta por el 20% [Retención astuta]


Existe una frase popular que dice que trabajar es algo feo, tanto así que tienen que pagarnos para hacerlo. Y seamos honestos, si efectuáramos una encuesta a nivel nacional con una simple pregunta: “si pudiera dedicar su tiempo, esfuerzo y capacidades a otra actividad diferente a la que hace en su trabajo, sin tener que preocuparse de sus entradas monetarias ¿Lo haría?”. Probablemente un 99.9% respondería que sí. Yo en mi caso si o haría. Por eso se dice que el trabajo que uno debe buscar es un tipo de intersección entre lo que me gusta y lo que realmente me puede dar une remuneración efectiva. No significa tampoco que la gente en general odie sus trabajos. Yo no lo hago. Pero sí en muchas ocasiones nos sentimos un poco desmotivados porque el trabajo no parece tener un sentido más trascendental. Por ello como mencioné en la introducción, muchas personas están dispuestas a trabajar de gratis para programas informáticos altamente valiosos que regalan al mundo, solo por la satisfacción de trabajar en algo que parece tener mayor propósito. Por otra parte existe también en este fenómeno, una cierto sentido egocéntrico. Dependiendo del tipo de compañía, muchas veces el esfuerzo de un empleado, dígase por su nivel de relevancia, tipo de trabajo o simplemente anonimato intrínseco en el puesto, no se ve reflejado en algo que él pueda sentir que es importante. Sin embargo hay una compañía que como en muchas otras cosas, ha marcado huellas muy visibles como para que otras compañías sigan el camino.

Si en alguna ocasión han tenido la mala fortuna de ver uno de los videos que hay en YouTube donde se muestra el ambiente de trabajo que existe en las instalaciones de Google, y digo mala fortuna porque nos da bastante envidia, hay que confesarlo, se habrán dado cuenta que a Google le gusta mantener motivado a sus empleados. Almuerzo gratis, salas de juego, masajista de la empresa, área de trabajo personalizada al gusto del empleado, en fin, no se sabe si es una empresa o un centro de recreo. Uno diría, ¿Qué más puede pedir un empleado en la vida? Ya ganan bien y tras de eso tienen unos de los mejores lugares para trabajar en todo el globo. Quizás por ello Google ha sido tan exitosa reteniendo los mejores trabajadores que una compañía de alta tecnología requiere.

Los psicólogos que estudian el comportamiento del empleado, han encontrado que existen dos tipos de motivaciones. Las externas o extrínsecas, y las internas o intrínsecas. Las motivaciones extrínsecas vienen fuera del empleado, esencialmente a través de premios como bonos, aumentos o cambios de responsabilidades. Por otra parte las motivaciones intrínsecas provienen de adentro del empleado, como el interés propio de un empleado en terminar una tarea, y la satisfacción que viene de hacer un trabajo bien hecho.

Como hemos visto, Google al igual que muchas otras compañías reconoce la efectividad de usar motivaciones externas. Pero no solo se quedan ahí. La empresa ha caído en conciencia desde hace mucho tiempo que los mejores empleados, usualmente también los mejor pagados, y también los más cotizados en el mercado, necesitan algo más que papel verde para sentirse a gusto en su trabajo. Google necesitaba adaptar esa misma motivación que se encuentra en el código de código abierto que donan miles de personas alrededor del mundo, a una versión más corporativa.
Cabe abrir un paréntesis antes de continuar con la hermosa historia de Google. Según la teoría de diseño organizacional, existen cuatro tipos de estrategias competitivas en la que una compañía debe focalizarse en una para triunfar en su mercado respectivo. Estas estrategias se les conocen como estrategias de Porter.




Figura 1 Estrategias competitivas de Porter

Google es claramente una empresa cuya estrategia es la diferenciación muy marcada de sus productos. Su producto inicial y producto estrella, su buscador de internet, es el buscador por excelencia. Tanto es así que en la actualidad existe un verbo no reconocido aún de manera formal en el lenguaje, para indicar hacer una búsqueda en Internet: “googlear”. La mayoría de las empresas que llevan esta estrategia de competitividad requieren contar con lo mejor del recurso humano. Esto implica tener una estrategia consolidada para la retención del personal. Las empresas que compiten por su bajo costo, generalmente tienden a tener un nivel medio –bajo de inversión en motivación del personal, dado que requieren mantener costos bajos de producción para poder mantener los costos bajos en sus productos. Es por ello que el modelo de Google tampoco puede pretender aplicarse a cualquier compañía. Todo depende del tipo de objetivos que se trace a nivel organizacional. Cerramos paréntesis.

Google buscó entonces agregar valor motivacional intrínseco a la experiencia laboral de sus empleados adaptando un modelo que primeramente tomó la compañía 3M. Los departamentos de investigación de 3M comenzaron a aplicar lo que ellos llamaron “la regla 15 de 3M”. Esta regla consiste en que un empleado puede invertir un 15% de su tiempo en proyectos de su propia escogencia, pudiendo ser también ideas propias, totalmente originales, sin la presión de saber que su proyecto tiene que ser un éxito. Aparentemente esta regla ha sido tan exitosa, que productos muy reconocidos en la actualidad, y altamente rentables, han surgido de iniciativas tomadas en el tiempo de la regla 15. Un ejemplo son las muy conocidas notas “post-it” (pequeñas hojas de papel adhesivas para mantener pequeñas notas al alcance).

La regla de 15 fue entonces adaptada por Google agregándole 5% más. La regla 20%. Los desarrolladores en Google pueden tener toda la libertad de dedicar 20% de su tiempo en conceptos que quizás siempre han tenido en la mente, pero quizás nunca han podido darle inicio por la restricción que tenían en sus trabajos anteriores, con una gran jornada agotadora que demanda dedicación absoluta a proyectos de alta prioridad. Hay que hacer la aclaración quizás algo obvia, de que a pesar que los ingenieros tienen toda la libertad de elegir el área de investigación en la que desean poner su empeño, todo el trabajo debe siempre procurar estar alineado con los objetivos de la compañía.

Con esta estrategia ambos, Google y sus empleados, obtienen muchos beneficios. Voy a citar solamente algunos para ejemplificar:
• La estrategia es altamente atractiva para recién graduados de la universidad los cuales Google puede contratar. Los jóvenes ven la oportunidad de mantener cierto nivel de autonomía.
• La regla logra atraer a desarrolladores del mundo del código abierto, los cuales son generalmente muy reconocidos como genios de la programación.
• La regla ayuda a acelerar el trabajo en los proyectos regulares, dado que los desarrolladores desean liberarse lo más pronto posible para dedicar tiempo a sus proyectos personales.
• Google se ha beneficiado al igual que 3M, obteniendo productos muy valiosos como el “Adsense” (publicidad contextualizada).

No cabe duda por la cual la mayoría de nosotros, los profesionales en informática, soñamos con que Google nos contrate o al menos nos deje hacer algún nivel de pasantía en esa tierra que parece un más un invento de película, que la realidad de una compañía multimillonaria.

Las comunidades inteligentes [Desarrollo del empleado]


Las compañías contratan gente brillante, con un alto IQ y luego la organización tiende a desecharlos por políticas y prácticas que inhiben la creación de valor.”3

Quisiera concluir con una idea moderna que se está comenzando a aplicar en la empresa en la que laboro. A esta idea se le conoce como “Smart Communities” o Comunidades Inteligentes en español. Este concepto surge de toda una teoría más general que propone el fortalecimiento de la organización inteligente. Como se lee en la cita de arriba de los promotores de esta idea, muchas veces las compañías, a pesar de ser buenas contratando los mejores recursos, son torpes logrando retenerlos. La retención siempre significa algún grado de pérdida para la empresa. Sobre todo si la curva de aprendizaje de sus cargos de trabajo son altamente pronunciadas.

El profesional de hoy en día necesita mayor motivación para permanecer en una empresa. Ya sabemos que las motivaciones externas son valiosas, pero las internas también son necesarias para que el empleado sienta que la empresa lo valora, y no que piense que es simplemente una tuerca que la empresa puede reemplazar con relativa facilidad. Una forma en que las compañías pueden en el presente ayudar a cultivar la motivación intrínseca de sus empleados, a la misma vez que se ven beneficiadas con un espíritu emprendedor de grupos de expertos, es por medio de las comunidades inteligentes.

El concepto de las comunidades inteligentes busca que se establezcan grupos especializados en los diferentes ámbitos profesionales en los que se desenvuelve la empresa. Estos grupos los forman las personas más expertas o antiguas de sus respectivas ramas. A ellas se les puede delegar la coordinación de muchas tareas que tienen valor para el departamento de recursos humanos. Por ejemplo la comunidad puede coordinar la revisión y actualización de las descripciones de puesto del área en el que trabajan. También se les delega la potestad de redefinir como va ser el crecimiento profesional del empleado dentro de la empresa. Cuáles pasos van a tener que tomar para ir avanzando hacia mejores posiciones y responsabilidades. Esto incluye definir qué capacitación interna va tener que llevar para escalar en el nivel en el que se encuentra.

Otra ventaja competitiva que se obtiene con esta iniciativa, es la de consolidar un grupo que también se dedique a la investigación continua. En el mundo de las tecnologías de información, la única constante es el cambio. Y por ello una empresa que se dedique a brindar servicios informáticos, requiere un continuo compromiso en mantenerse actualizado en las últimas tendencias tecnológicas. De lo contrario pierde competitividad. La comunidad puede administrar sus miembros, siendo ella la que conoce más de su nicho, para que hagan investigación que repercute en formación de los empleados de la empresa.

Como se estudia en la teoría organizacional moderna, las organizaciones actuales se ven enfrentadas al reto de estar inmersas en un mercado altamente cambiante. Las estructuras clásicas altamente jerárquicas necesitan ser reemplazadas por estructuras más planas y versátiles. El uso del instrumento de las comunidades inteligentes puede ser un medio muy efectivo para iniciar este cambio que muchas veces les cuesta a las empresas tomar.

Conclusiones


He mencionado en este escrito varias de las tendencias actuales de manejo de recurso humano, que pueden caer dentro de uno de las dos tendencias actuales del siglo XXI. Valor cada vez mayor de la información, y la creciente demanda, quizás de manera inconsciente, de mayor valor intrínseco proveniente de los cargos que toman los empleados de las corporaciones. Muchas empresas ya están utilizando el fácil acceso a la información que hay en la web de los perfiles profesionales de las personas. Esto ha llegado a ser de mucha ayuda a las empresas que no necesitan gastar tanto dinero como antes en anuncios clasificados en periódicos nacionales. Aún una empresa puede ir más allá de sus fronteras y analizar perfiles profesionales de personas en el extranjero. Las fronteras cada vez se van haciendo más chicas.

Se expuso además el caso de Google. De cómo una empresa ha sido visionaria y pionera en reconocer el alto valor de su recurso humano al invertir, no solamente en las recompensas clásicas externar como aumentos salariales y promociones, sino que también ha dado un valor al sentido inherentemente humano de querer emprender proyectos personales, al permitir que sus colaboradores dediquen tiempo de la empresa a proyectos propios que luego la misma puede tomar gran ventaja. En el último apartado mencioné un poco de la iniciativa que ya se está tomando en la empresa, en la que este su servidor brinda sus servicios profesionales, para avanzar a una idea de una organización más inteligente. Y la inteligencia no viene de equipos más caros, sino de personas que se sientan más a gusto dando más de sus capacidades en beneficio de la empresa, que al final terminan siendo beneficio para ellas mismas.

Es evidente que el mundo corporativo está cambiando, y lo sigue haciendo en medidas aún quizás insospechadas para muchos. Ya grandes compañías han tomado ventaja de ello midiendo los tiempos actuales y tomando decisiones “locas” si se comparan con las normas clásicas. Eso les ha permitido adueñarse de lo que verdaderamente es, y debería ser en la mente de la alta gerencia de una manera más palpable, el recurso más valioso de una compañía. Su capital humano. En el mundo de la información, las compañías que quieren diferenciarse apuntando a ser las mejores de su mercado, necesitan saber que requieren captar lo mejor de lo mejor, pero considero aún más crítico, retener lo mejor de lo mejor. A veces un profesional no era el mejor cuando fue contratado, pero tenía potencial que supo aprovechar para convertirse en la pieza clave. El problema es con el tiempo, un profesional de la modernidad, donde cada vez tenemos que preocuparnos menos por muchas de nuestras necesidades básicas, terminamos volviendo la mirada a las necesidades un poco más trascendentales; las que nos generan una satisfacción de tener propósito en lo que hacemos.

Estamos a la puerta de que los altos jerarcas de todas las compañías del mundo, y principalmente las de países en vías de desarrollo como el nuestro, sean más consientes de los tiempos en los que vivimos. Claro está que deben hacerlo si quieren ser exitosos en un mercado cada vez más globalizado y sumamente competitivo. De lo contrario pueden seguir tranquilos esperando que nadie les robe sus recursos humanos, apostando a la conformidad y “lealtad” de sus empleados.

Bibliografía


1. “La Gestión de los Recursos Humanos. Cómo atraer, retener y desarrollar con éxito el capital humano en tiempos de transformación” Simon L.Dolan, Ramón Valle Cabrera, Susan E. Jackson y Randall S. Schuler. Tercera Edición. Editorial Mc Graw Hill. 2007

2. “Teoría y diseño organizacional” Richard L. Daft. Novena Edición. Cengage Learning Editores. 2007.

3. “The Smart Organization. Creating Value through strategic R&D” David Matheson y James E. Matheson. Primera Edición. Harvard Business Press. 1997.

4. “The Google Way: How One Company Is Revolutionizing Management as We Know It” Bernard Girard. Primera Edición. No Starch Press. 2009.

5. Imagen de cuadro de estrategias de Porter. Extraído el 1 de Marzo del 2011.

6. How Corporate Recruiters Gain a Competitive Edge with LinkedIn? Extraído el 1 de Marzo del 2011.

domingo, 27 de febrero de 2011

Más que un juguete de geek...


En el tiempo que llevo de desenvolverme en el ámbito de las tecnologías de información, tomando en cuenta mi formación universitaria y experiencia laboral, siempre he considerado que no calzo por completo con el perfil de un geek. Bueno, habría que definir a que me refiero con geek en este contexto, ya que este es un calificativo que hoy en día se usa de manera muy libre. Me refiero a que no he sido tan geek desde el punto de vista de mantenerme muy actualizado con las novedades en gadgets. No soy el tipo de persona que quiero cambiar de PC cada 2 o 3 años. De hecho, mi computadora de escritorio lleva conmigo casi 10 años. Ya está pidiendo jubilación pero aún no he querido dejarla ir, porque a la verdad, no he sentido necesidad de hacer un cambio. Y es que siendo que en la universidad siempre tenía acceso a las computadoras de los laboratorios, y ahora en el trabajo he tenido mi computador personal y en ocasiones una laptop que puedo llevarme a casa, no veo una gran urgencia de buscar refrescarme tecnológicamente. Pero aún si lo hubiese requerido, siento que no soy el típico geek que por ejemplo ahorra parte de su salario para comprarse lo último en aparatos sofisticados como un Ipad .

También todos mis celulares hasta hace poco eran una versión bonita de un ladrillo. No es que sea mal agradecido con ellos, solo quiero decir que comparados con sus contemporáneos más modernos, realmente sí eran ladrillos en aspectos de tecnología. Sin embargo recientemente creo que estoy modificando un poco ese patrón conservador a la modernidad, no por un aspecto de sed consumidora, sino que creo de estrategia profesional. Doy gracias a Dios por mi nuevo aparatito, acabo de adquirir un celular HTC Legend el cual lo tengo actualizado con la más reciente versión 2.2 de Android. En verdad me deje seducir el oído con los comentarios y revisiones acerca de los celulares con sistema operativo Android. Debo confesar que mi impulso consumista se reparte en varios porcentajes: 40% en sentirme relegado con los compañeros de trabajo con sus Iphones y demás celulares inteligentes (lo que mi esposa llama el síndrome Quico), otro 40% en sentir la necesidad de contar con un aparato más sofisticado (“necesidad creada”, pero necesidad a fin de cuentas) y otro 20% en conocer la plataforma de la cual todos hablan hoy en día. ¿Solo un 20%? Pues sí, casi, casi compro un llamativo Iphone. En todo caso, y ya aterrizando esta corta disertación, creo que adquirir este celular ha sido más que un juguete nuevo, una verdadera inversión.

Ya hace un tiempo tenía la inquietud de comenzar a reservar neuronas para el desarrollo de móviles, pero no ha sido hasta que he tenido la experiencia de usar la enorme cantidad de aplicaciones que hay para el Android, que he comprendido el salto tecnológico que estamos experimentando en la interacción humano-computador. Aún teniendo una laptop que puedo llevarme a la cama, casi nunca siento ganas de prenderla para hacer cualquier cosa con ella. Casi siento el mismo nivel de esfuerzo de ir a sentarme al escritorio donde tengo la PC. Pero en cambio la experiencia de un celular con aplicaciones está a varias pulsaciones de distancia. Es más fácil comprender por qué tanto escándalo con este tipo de celulares cuando se tiene uno a mano. Sin duda el desarrollo para este tipo de aparatos va ir cobrando cada vez mayor relevancia, y tenemos que estar preparados para ello.

Así que a codificar se ha dicho…

lunes, 21 de febrero de 2011

Código para acomodar imagenes por fecha de modificación

Quize crear un programa pequeño para organizar un montón de fotos que tengo desordenadas en distintas carpetas. En lo particular me gusta tener las fotos almacenadas en carpetas que indiquen la fecha en que se tomaron. De esa manera puedo hacerme una idea cronólogica de cuándo se fueron tomando. Claro está que este código funciona bajo la premisa de que la fecha de modificación de las fotos es la misma de cuándo se tomaron, que es la misma de cuando se descargaron. Puede ser que no sea el caso de muchos archivos, pero al menos ayuda agregando cierto nivel de orden.


Clase Principal:


package gsolano.photoorganizer;

import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* This class is used to copy files from a specific folder (recursive scan in that folder)
* into a new folder structure using modification date of the files to map them correctly.
*
* @author gsolano
*
*/
public class FileOrganizerByModificationDate {

/**
* Extensions to that will be processed.
*/
protected final String [] extensionsToProcess = {"jpg","avi"};
/**
* Year(key): 2010
* ++ Date(key): 20100125
* ++++ File: 010223.jpg
* ++++ File: 232455.avi
*/
protected Map<Integer, Map<String,List<String>>> newFolderStructure;

/**
* Directory path that will be scanned.
*/
protected String pathToProcess;

/**
* Counter of total files found.
*/
protected int totalFilesToOrganize;

/**
* Default constructor.
*/
private FileOrganizerByModificationDate() {
newFolderStructure = new HashMap<Integer,Map<String,List<String>>>();
totalFilesToOrganize = 0;
}

/**
* Constructor receiving path that will be processed.
* @param pathToProcess
*/
public FileOrganizerByModificationDate(String pathToProcess){
this();
this.pathToProcess = pathToProcess;
}

/**
* Scans the path provided in the constructor.
*/
public void scan() {
if(pathToProcess != null && pathToProcess != "") {
scan(pathToProcess);
}
}

/**
* Scans a specific path.
* @param rootDirPath
*/
public void scan(String rootDirPath) {
System.out.println("Scanning: " + rootDirPath);
this.pathToProcess=rootDirPath;
File sourceDir = new File(rootDirPath);

if(sourceDir.exists() && sourceDir.isDirectory()) {
scanRecursively(sourceDir);
System.out.println("Files to organize: " + totalFilesToOrganize);

} else {
System.out.println("Directory does not exist!");
}

if(newFolderStructure.size() > 0) { // Files were added to the new structure?
try {
System.out.println("Copying files...");
copyFiles(); // Copy the files in the new directory structure.
} catch (IOException e) {
e.printStackTrace();
}
// Clean the class..
newFolderStructure.clear();
totalFilesToOrganize = 0;
}
}

/**
* Once scan is finished, this method is in charge of copying files to new structure.
* @throws IOException
*/
private void copyFiles() throws IOException{
Set<Integer> years = newFolderStructure.keySet();
int totalFilesCopied = 0;

for(Integer year : years) {
String newYearDirectoryPath = (pathToProcess.endsWith(File.separator) ? pathToProcess + year.toString() :
pathToProcess+ File.separator + year.toString());
// Let's create the year directory.
File newYearDirectory = new File(newYearDirectoryPath);
newYearDirectory.mkdir();
String newYearAbsoluteDirectoryPath = newYearDirectory.getAbsolutePath();
Set<String> newDateFolders = newFolderStructure.get(year).keySet();

// Diving into the years map.
for(String newDateFolder : newDateFolders) {
// Let's create the date folder.
String newDateFolderPath = newYearAbsoluteDirectoryPath + File.separator + newDateFolder;
File newDateDirectory = new File(newDateFolderPath);
newDateDirectory.mkdir();

List<String> files = newFolderStructure.get(year).get(newDateFolder);

for(String file : files) {
// Finally, copy the file in the respective folder.
FileCopy.copy(file, newDateFolderPath, false);
totalFilesCopied++;
System.out.print("\r"+progress(totalFilesCopied, totalFilesToOrganize));
}
}
}

}

/**
* Recursive method to find empty folders.
* @param sourceDir
*/
private void scanRecursively(File sourceDir) {
// List files and directories in the first level.
File[] listOfFiles = sourceDir.listFiles();

if (listOfFiles != null) {
for(int i=0; i < listOfFiles.length; i++) {
if (listOfFiles[i].isFile()) {
String extension = getExtension(listOfFiles[i].getName());
if (isValidExtension(extension)) {
processFile(listOfFiles[i]);
totalFilesToOrganize++;
}
}
else if(listOfFiles[i].isDirectory()) { // Recursive call.
scanRecursively(listOfFiles[i]);
}
}
}
}

/**
* Process the file found. The file path is added to the new map structure used later to create
* the new directory structure and copy the file.
* @param file
*/
private void processFile(File file) {

DateFormat dfYearOnly = new SimpleDateFormat("yyyy");

Date lastModified = new Date(file.lastModified());
Integer yearOfFile = Integer.valueOf(dfYearOnly.format(lastModified));
DateFormat df = new SimpleDateFormat("yyyyMMdd");
String dateFolder = df.format(lastModified);

if(!newFolderStructure.containsKey(yearOfFile)) {
newFolderStructure.put(yearOfFile, new HashMap<String, List<String>>());
}
if(!newFolderStructure.get(yearOfFile).containsKey(dateFolder)) {
newFolderStructure.get(yearOfFile).put(dateFolder, new LinkedList<String>());
}
newFolderStructure.get(yearOfFile).get(dateFolder).add(file.getAbsolutePath());
}

/**
* Get file's extension *
* @param fileName
* @return file extension
*/
private String getExtension(final String fileName) {
final int x = fileName.lastIndexOf(".");
return (x > 0 && x < fileName.length()) ? fileName.substring(fileName.lastIndexOf(".")+1, fileName.length()) : "" ;
}

/**
* Validates if the extension is valid to be processed.
* @param extension
* @return
*/
private boolean isValidExtension(String extension) {
for(String extensionToProcess : extensionsToProcess) {
if(extensionToProcess.equalsIgnoreCase(extension)){
return true;
}
}
return false;
}

/**
* Simple function to calculate progress of the copy process.
* @param numerator
* @param denominator
* @return
*/
private String progress(int numerator, int denominator) {
float progress = 0;
progress = ((float)numerator / denominator)*100;
DecimalFormat df1 = new DecimalFormat("##.00");
return df1.format(progress)+"%";
}

public static void main(String[] args) {
FileOrganizerByModificationDate organizer =
new FileOrganizerByModificationDate(args[0]);
organizer.scan();
}
}



Clase para hacer el copiado:


package gsolano.photoorganizer;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
* Utility class to copy a file from a source path to a target path.
*
* @author gsolano
*
*/
public class FileCopy {

public static void copy(String fromFileName, String toFileName,
boolean overrideFiles) throws IOException {
File fromFile = new File(fromFileName);
File toFile = new File(toFileName);

if (!fromFile.exists())
throw new IOException("FileCopy: " + "no such source file: "
+ fromFileName);
if (!fromFile.isFile())
throw new IOException("FileCopy: " + "can't copy directory: "
+ fromFileName);
if (!fromFile.canRead())
throw new IOException("FileCopy: " + "source file is unreadable: "
+ fromFileName);

if (toFile.isDirectory())
toFile = new File(toFile, fromFile.getName());
if (toFile.exists()) {
if (!toFile.canWrite()) {
throw new IOException("FileCopy: "
+ "destination file is unwriteable: " + toFileName);
}
if (!overrideFiles) {
// File exist, do not override it.
return;
}

String parent = toFile.getParent();
if (parent == null)
parent = System.getProperty("user.dir");
File dir = new File(parent);
if (!dir.exists())
throw new IOException("FileCopy: "
+ "destination directory doesn't exist: " + parent);
if (dir.isFile())
throw new IOException("FileCopy: "
+ "destination is not a directory: " + parent);
if (!dir.canWrite())
throw new IOException("FileCopy: "
+ "destination directory is unwriteable: " + parent);
} else {
// Create directory.
new File(toFile.getParent()).mkdirs();
}

FileInputStream from = null;
FileOutputStream to = null;
try {
from = new FileInputStream(fromFile);
to = new FileOutputStream(toFile);
byte[] buffer = new byte[4096];
int bytesRead;

while ((bytesRead = from.read(buffer)) != -1)
to.write(buffer, 0, bytesRead); // write
} finally {
if (from != null)
try {
from.close();
} catch (IOException e) {
;
}
if (to != null)
try {
to.close();
toFile.setLastModified(fromFile.lastModified());

} catch (IOException e) {
;
}
}
}
}


Salida: