
Tuesday April 26, 2005
Crystal Reports XI y JAVA
Ya esta disponible Crystal Reports XI en español de los chicos de Business Objects. Esta herramienta no necesita presentacion. Todos sabemos que son los lideres en herramientas de reporting y que llegan donde desfallecen Jasper Reports y demas, los que no son tan conocidos son sus intentos por acercarse al mundo web.
Su primera aproximacion fue Crystal Enterprise X, una especie de "cataplasma" para Crystal Reports X que hacia de repositorio y front-end web de los informes generados en Crystal y tenia un motor que te generaba los mismos en DHTML, PDF, etc. No estaba mal, pero su arquitectura era un desastre, mezclando DLLs con JARs y lindezas por el estilo.
La sorpresa es que Crystal Enterprise desaparece como tal -a un año de su salida, ole tus huevos- y pasa a denominarse Crystal Reports XI Server. Esta informacion que facilito, sera util para los que ya trabajaban con estos sistemas y, como yo, desearan actualizarse y el cambio de nomenclatura les ha dejado un poco "descolocados".
Aun es pronto para evaluar que novedades -relativas al mundo JAVA- pueden aportar estas herramientas, pero, de momento, la instalacion de las aplicaciones WEB en un simple TOMCAT se ha simplificado notariamente. Supongo que las APIs para recuperar, programar, modificar y visualizar reports con JAVA tambien habran sido mejorados (ya desde la version X se permirte establecer como fuente de datos de un informe de Crystal una clase JAVA, por ejemplo).
Tambien es destacable que los licenciatarios de Crystal Enterprise Professional con soporte contratado no son actualizados al ya mencionado Crystal Reports XI Server sino a Business Objects Enterprise que, ademas de las funcionalidades de repositorio y front-end añade funcionalidades de Business Inteligence. Mención aparte merece el TORTUOSO PROCESO necesario para que te actualicen tus claves de licencia del anterior producto a este, pero eso merece ser contado en otro post.
Todos estos productos estan disponibles para descarga en la web de Business Objects asi que, si necesitais herramientas de reporting avanzado del tipo quiero un informe de ventas semanal que se realice automaticamente todos los lunes a las 9 de la mañana en formato PDF y se envie, tambien automaticamente, a n direcciones de correo ya sabeis de donde podeis tirar. Se integra con JAVA -aunque nadie haya dicho que sea facil- y... funciona !!!.
(2005-04-26 08:53:21.0)
Permalink

Monday April 04, 2005
¿ Bugcito en log4j ?
Yo, keko y muchos mas extendemos log4j para ampliarlo y adaptarlo a nuestras aplicaciones. En concreto, yo no utilizo el objeto Logger sino un objeto Log que extiende del mismo y facilita la gestion de push y pop del mismo entre otras muchas cosas.
Para poder utilizar tu propia clase extendida de Logger tienes que crear una clase que herede de LogFactory y, en concreto, sobreescribir el metodo makeNewLoggerInstance que devuelve un Logger para que devuelva una nueva instancia de tu objeto extendido. Despues, cuando quieres instanciar un objeto extendido de Logger, en vez de hacer el clasico Logger.getLogger(String) utilizas Logger.getLogger(String, LogFactory) donde LogFactory es tu factoria extendida.
Como veis, sencillo y elegante, sin embargo algo obscuro se esconde bajo la superficie ;).
Normalmente, cuando se instancia un log, se califica el mismo con el nombre de la clase en la que se instancia (Ej. Logger.getLogger("com.foo.Bar")) aunque se puede calificar "solo" con el nombre del paquete, o con lo que queramos ("pepino", "jarroncho", etc.).
Nosotros, ademas de extender log4j tenemos un pequeño "cache" de logs que guarda las distintas instancias de Logger por eso, solemos calificar por paquetes en vez de por clases, se crea una instancia por paquete en vez de por cada una de las clases que contienen. Y AQUI PUEDE APARECER EL PROBLEMA.
Si te instancias un objeto Logger, calificandolo con el mismo nombre de la categoria que has puesto en tu log4.properties, te sale una CascotazoDeLaMuerteException. Es decir, imaginemos que en nuestro log4j.properties tenemos una categoria tal que asi:
- log4j.logger.com.varma.util.database=DEBUG
Bien, cuando yo me instancio mi objeto que hereda de Logger (en mi caso Log) puedo hacer esto:
Log log = Log.getLog("com.varma.util.database.otropaquete");
o esto otro:
Log log = Log.getLog("com.varma.util.database.Miclase");
Pero como haga esto, la aplicacion se me muere;
Log log = Log.getLog("com.varma.util.database");
¿ Curioso ? ¿ Os ha pasado ?. La verdad es que me parece demasiado increible como para que sea cierto, seguiremos investigando.
(2005-04-04 13:22:17.0)
Permalink

Monday February 07, 2005
Pasando de Struts 1.0.X a 1.1 y no morir en el intento.
Hace poco quise hacer unos retoques en una utilityde mi proyecto relacionada con la gestion de errores. Basicamente, queria utilizar los mensajes de error con parametros que proporciona el framework Struts y su clase ActionError, para que admitiera mensajes con un numero ilimitado de parametros.
El caso es que la version de Struts con la que trabajamos era la version 1.0.2 y, en esta version, la API de Struts te da la posibilidad de trabajar con 1,2,3 y 4 parametros pero no mas asi que... ¡Que mejor que esta ocasion para actualizarse!. Craso Error.
A los actualizadores compulsivos, a los que nunca leen las Release Notes (como yo) y a los echaos p'alante... ¡¡¡ QUIETORRRL !!!. La actualizacion no es tan sencilla como actualizar un jar por otro y puede ser bastante traumatica.
Espero que esta enumeracion de "chinitas" pueda servirle a alguien que tenga que pasar por tan traumatica experiencia:
¿ Donde esta mi jar y quien es esa gente ?
¿ Te esperas un struts.jar sencillo y facilito ? Ayyyy alma candida... a partir de la version 1.1, los de JAKARTA decidieron que habia mucho (pero mucho) utilizable dentro de Struts y ademas utilizado por muchos de sus otros proyectos. Para limitar la dependencia de estos de Struts se sacaron un conjunto de librerias de utilidades del "antiguo" Struts. Habian nacido las librerias Jakarta Commons.
Asi que, ademas de el jar de Struts ya os estais descargando e importando todas estas porque, sino, Struts no os va ni a compilar. Es como las lentejas. Si quieres lo tomas y, si no, lo dejas.
Struts es mio y depreco lo que me sale de los c******
Pues si. Y eso ha debido pensar algun guru de JAKARTA... "y lo que no quiero deprecar pero me molesta lo elimino" ... y living la vida loca. Asi que, si habeis hecho un uso mas o menos exhaustivo de Struts ya os podeis preparar para sustituir y rebuscar por la API hasta que deis con algo parecido.
Tu DataSource te lo picas tu.
Antes Struts daba soporte a un conjunto de utilidades para el acceso a Base de Datos. Ahora ya no. Y, como dicen las feministas, NO MEANS NO. No esta deprecado, ni desactualizado, ni desfasado... simplemente NO esta. Lo han quitado por si te entran tentaciones de seguir utilizandolo. En concreto, nosotros hutilizabamos el GenericDataSource y la clasecita, una vez extendida y retocada nos ha dado mucho juego. Afortunadamente, la implementacion de la misma es monolitica y hereda de javax.sql.Datasource y poco mas asi que, si lo deseais, podeis meter la clase directamente en vuestro CLASSPATH y se soluciono el problema.
LOG4J utilizaste... la cagaste
Y esto es lo mejor... Struts deja de funcionar porque no se loga bien con... ¡ tu log4j !. Si amigos, es lo bueno de utilizar la libreria Commons Logging que como encuentre log4j en tu CLASSPATH va y lo utiliza, y claro... tu que tenias tu log4j bien configurado mediante tu properties en un directorio fuera de tu CLASSPATH (porque claro, tus properties los quieres dejar fuera de un supuesto y futuro .war para que puedan ser modificados en caliente) y te curras tu Factoria de Logs que trinca el properties con un configureAndWatch y te has montado el invento del siglo y... ahora resulta que viene Struts, que se carga antes que tu aplicacion y UTILIZA PORQUE SI el log4j y... este todavia no se ha configurado y CASCOTAZO. Asi que, o te pones a hacer reingenieria inversa y averiguas como arreglas el embolao o te das cuenta de que tienes que meter el log4j en el CLASSPATH para que Struts sencillamente cargue. En dos palabras: IN-CREIBLE.
Como conclusion, que cambiar de Struts 1.0.X a 1.1 puede ser bueno pero, desde luego, ni bonito ni barato. Aun con todo, es una decision muy a tomar en cuenta porque la version 1.1 ha sido la gran revolucion a nivel de Struts y, una vez migrado a esta, no hay coste para cambiar a versiones posteriores como la 1.2. No, no me arrepiento de todo el embolao al que me han arrastrado pero, la verdad, siendo Struts un framework tan extensamente utilizado, el cambio de version me ha parecido chapuceril, agresivo y complicado. Quizas todo sea culpa mia, quizas era mi obligacion estar al tanto de todo lo que suponia cambiar de version pero, la verdad, los diseñadores de JAKARTA me tenian muy bien acostumbrado. De momento yo, con la version 1.1 voy a aguantar tooooodo lo que pueda.
(2005-02-07 21:00:45.0)
Permalink

Monday October 11, 2004
Clases de Cocina: Barra de Progreso JAVA
Muchas veces, todos hemos deseado utilizar una Barra de Progreso para monitorizar el progreso de determinadas tareas en nuestras aplicaciones WEB. Poco a poco, vamos creando procesos mas y mas complejos (y mas y mas pesados) en nuestras aplicaciones y, la verdad, cuando el Front-End de las mismas esta basado en un navegador, tener a un usuario esperando 2,5 o 10 minutos sin indicarle QUE esta pasando no da muy buena sensacion.
A continuacion, voy a plantear una implementacion, sencilla y elegante, de una barra de progreso basada en navegador WEB y un simple mecanismo para ir marcando el progreso de una tarea que reflejara dicha barra. Como el post va para largo, lo he escrito a lo Arguiñano, asi espero que os sea mas amena su lectura. Yo, desde luego, me he divertido escribiendolo.
Hoy vamos a explicar como un implementar, en una aplicacion web JAVA, una barra de progreso rica, rica y con fundamento.
Hay muchas maneras de realizar este plato y, por supuesto, cada uno de vosotros le podeis dar vuestro propio toque personal.
Yo os voy a explicar como la cocino yo, tal y como me la enseño mi madre.
Para cocinar la Barra necesitaremos los siguientes ingredientes:
- 1 Servlet bien fresco
- 150gr de JavaScript
- 1 par de clases de Java ligeras y esponjosas
- Salsa tipo "Patron Commando" para aderezar
Empezaremos por picar muy fino las classes Java. A estas clases JAVA yo les echo "Patron Commando" para que queden mas agradables al gusto.
El patron Commando, bien utilizado -como diria el Chef Albert du Vilch-, puede ser muy util combinado con el patron factoria para cambiar el funcionamiento de toda una aplicacion a golpe de Property, sin embargo, no es este hilo el lugar mas apropiado para que hablemos de el. Podeis conseguir mas informacion, por ejemplo, aqui.
Nos creamos una interfaz que implementara el Patron Commando. Esta interfaz servira para definir los metodos que necesitemos para marcar el progreso de una tarea a traves de nuestro codigo:
public interface ProgressBox {
String task = null;
/**
* Devuelve la tarea que se esta monitorizando.
* @return String el ID de la tarea
*/
public String getTask();
/**
* Establece el porcentaje de finalizacion de una tarea.
* @param percentage double el porcentaje de finalizacion de la tarea
*/
public void setProgress(double percentage);
}
|
Por supuesto, si se desea, se le puede añadir a esta clase un monton de "especias" que realcen el sabor y, asi, podemos definir metodos que calculen el progreso de una tarea en base a un numero de iteracion dentro de un bucle u otro que lance un mensaje (para trasmitir un mensaje de error, por ejemplo, en vez de un porcentaje). Esto se deja a vuestro buen criterio.
Una vez que tenemos la interfaz bien picadita, vamos a empezar a amasar una clase JAVA que la implemente.
Lo bueno del patron Commando es que te permite hacer llamadas a procedimientos sin saber que es lo que realmente estas haciendo. Asi, podemos crear una clase que implemente la Interfaz y que guarde el progreso de una tarea como cada uno desee (HTTPSession, Base de Datos, Property, etc.).
Yo voy a "amasar" una clase que guardara el progreso de una tarea en una Session HTTP. Gracias a la interfaz que he definido previamente, podre distribuir este objeto por toda mi aplicacion, evitando tener que pasar como parametro el objeto Session, lo que supondria cepillarme cualquier logica de diseño en capas que puediera tener. A ver que os parece:
public class SessionProgressBox implements ProgressBox {
public static final String SESSION_PROGRESS_KEY = "task.progress.";
private HttpSession session = null;
private String task = null;
/**
* El constructor necesita una HTTPSession
* @param session HttpSession
* @param task String el ID de la tarea a controlar
*/
public SessionProgressBox(HttpSession session, String task) {
this.session = session;
this.task = task;
}
public void setProgress(double percentage) {
this.session.setAttribute(SESSION_PROGRESS_KEY + this.task, new Double(percentage));
}
public String getTask() {
return task;
}
}
|
Una vez tenemos nuestra interfaz y nuestra clase, las salpimentamos y las reservamos, para utilizarlas posteriormente.
Vamos a pasar a la parte mas dificil del plato: cocinar el Servlet. El Servlet de control de progreso es la parte mas importante a realizar puesto que el generara el codigo HTML y JavaScript que pintara la barra de progreso y seguira monitorizando el progreso.
Os presento un Servlet sencillito pero, eso si hecho de muy buena materia prima. No produce codigo compatible con Explorer y Netscape, solo con el primero pero, he preferido cocinarlo asi en aras de la claridad, para mejorar el entendimiento. Seguro que si alguien necesita compatibilidad doble sabra como dar su toque personal al plato.
public class ProgressServlet extends HttpServlet {
// Declaracion de variables
private static final String CONTENT_TYPE = "text/html";
private String millis2refresh = null;
private String millis2close = null;
/**
* Inicializacion de variables globales
* @throws ServletException
*/
public void init() throws ServletException {
millis2refresh = getServletConfig().getInitParameter("millis2refresh");
if (mil lis2refresh == null) {
millis2refresh = "1500";
}
millis2close = getServletConfig().getInitParameter("millis2close");
if (millis2close == null) {
millis2close = "2000";
}
}
/**
* Proceso de una peticion HTTP de tipo GET
* @param request HttpServletRequest
* @param response HttpServletResponse
* @throws ServletException
* @throws IOException
*/
public void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
response.setContentType(CONTENT_TYPE);
PrintWriter out = response.getWriter();
try {
// Se recuperan las variables del REQUEST
String idProceso = request.getParameter("idProceso");
String keyProgress = SessionProgressBox.SESSION_PROGRESS_KEY +
idProceso;
// Se recuperan variables de SESSION
Double progreso = (Double) request.getSession().getAttribute(
keyProgress);
if (progreso == null) {
progreso = new Double(0);
}
// Se compone la pagina web dinamicamente
out.println("< html >");
out.println("< head >");
out.println("< title >ProgressServlet< /title >");
out.println("< style >");
out.println(
"#fondobarra{position:absolute;left:100px;top:20px;background-color:#B7C3D0;}");
out.println(
"#barra{position:absolute;left:100px;top:20px;background-color:#3D59AB;}");
out.println("< /style >");
out.println("< /head >");
out.println("< body bgcolor=\"#ffffff\" >");
out.println(
"< div id=\"fondobarra\" style=\"width:100px;height:10px;z-index:9;\" >< /div >");
out.println(
"< div id=\"barra\" style=\"width:" + progreso +
"px;height:10px;z-index:10\" >< /div >");
out.println("< form action=\""+request.getContextPath()+"/progressservlet\" method=\"post\" >");
out.println("< input type=\"hidden\" name=\"progreso\" value=\"" +
progreso + "\"/ >");
out.println("< input type=\"hidden\" name=\"idProceso\" value=\"" +
idProceso + "\"/ >");
out.println("< br >");
out.println("< br >");
out.println(
"< p align=\"center\" >Completado un " +
progreso +
"% del proceso ...< /p >");
out.println("< /form >");
out.println("< /body >");
out.println("< script >");
out.println("if(document.forms[0].progreso.value < 100){");
out.println("setTimeout(\"document.forms[0].submit()\", " +
millis2refresh + ")");
out.println("}");
out.println("else{");
out.println("setTimeout(\"window.close()\", " + millis2close +
")");
out.println("}");
out.println("< /script >");
out.println("< /html >");
} catch (Exception ex) {
out.println("< html >");
out.println("< head >");
out.println("< title >ProgressServlet< /title >");
out.println("< /head >");
out.println("< body bgcolor=\"#ffffff\" >");
out.println("< br >");
out.println(
"< p align=\"center\" >Error al intentar monitorizar el progreso de una tarea: " +
ex+"< /p >");
out.println("< /body >");
out.println("< /html >");
}
}
|
Como podeis apreciar, es un Servlet bien sencillo, con no mucha complejidad. Pinta una barra de progreso utilizando capas (es decir, no viajan imagenes, la "pagina" generada apenas ocupa unos bytes con lo cual la percepcion de refresco, cada vez que va a consultar a servidor el progreso de una tarea, es imperceptible). Tambien escribe el codigo JavaScript necesario para que vuelva a lanzar una peticion al servidor en n milisegundos -los que le hayamos indicado en el Servlet- si el progreso es menor del 100% o que cerrara la ventana en caso contario.
El Servlet escribe el codigo HTML y JavaScript que deseis asi que, ya sabeis, si deseais darle un toque personal... a la sarten y vuelta y vuelta. Es mas, la unica concesion a las delicatessen son las dos variables de inicializacion del Servlet que indican en la configuracion del Servlet en descriptorescada cuantos milisegundos se refrescara el progreso de una tarea y cuantos segundos tardara en cerrarse la ventana una vez que se ha cumplido el 100% (variables millis2refresh y millis2close respectivamente).
He tenido que toquetar el codigo del Servlet para que el post quedara escrito para copiar y pegar (en concreto he tenido que poner un espacio al principio y al final de cada etiqueta HTML para que el weblog no se volviera loco) asi que, si encontrais algun problema en el mismo o algo que no entendais, ya sabeis donde encontrarme.
Una vez que tenemos el Servlet en el plato, procederemos a adornarlo con unas lineas de descriptor. En concreto las siguientes, que debeis colocar en el descriptor web.xml:
< servlet >
< servlet-name >progressservlet< /servlet-name>
< servlet-class >
 [escribir aqui el paquetizado de vuestro servlet Ej. com.test.].ProgressServlet
< /servlet-class >
< init-param>
< param-name >millis2refresh< /param-name >
< param-value >2000< /param-value >
< /init-param >
< init-param>
< param-name >millis2close< /param-name >
< param-value >3000< /param-value >
< /init-param >
< /servlet >
< servlet-mapping >
< servlet-name >progressservlet< /servlet-name >
< url-pattern >/progressservlet< /url-pattern >
< /servlet-mapping >
|
Mmmmm... ¡Que bien huele esto! ¡Ya lo tenemos casi listo!. Un poquito mas y el plato estara cocinado.
Ya hemos montado la estructura que monitorizara el progreso de una tarea ahora vamos a integrarlo con nuestra aplicacion web.
Nos vamos a la pagina web desde donde se lanzara nuestra aplicacion. Alli, dentro de un formulario tendremos seguro un campo del formulario de tipo submit, un campo de tipo button con un evento onclick controlado o algo similar. Bien, lo que tenemos que hacer es, antes de invocar el submit del formulario, llamar a la siguiente funcion:
function barraProgreso(idProceso)
{
window.open('<%=request.getContextPath()%>/progressservlet?idProceso='+idProceso, 'BarraProgreso', 'width=300, height=100, left=500, top=200, resizable=no, scrollbars=no');
}
|
Si os dais cuenta, esta funcion solo recibe un parametro que es un IDENTIFICATIVO UNICO de la tarea que quereis monitorizar. Lo ideal es que copieis esta funcion (y todas las funciones de JavaScript que utiliceis de manera general en toda vuestra aplicacion) en un fichero .js para poder importarlo en todas las paginas en donde haga falta en vez de copiar una y otra vez la misma funcion en todas las paginas.
Bien, el caso es que, lo hayais metido en un fichero .js o lo hayais copiado directamente en la pagina, ya podeis invocar a la funcion. Asi, por ejemplo:
< input type="button" value="Aceptar" onclick="barraProgreso(5);document.forms[0].submit()" />
|
E VOILA !!!. Una nueva ventana se abrira mostrando la barra de progreso en este caso, de la tarea "5".
Ahora solo falta marcar el porcentaje de ese progreso segun se van cumpliendo hitos de la tarea a monitorizar. Si no lo hicieramos, la barra no se moveria del 0% y la ventana que la contiene nunca se cerraria.
Vamos a mostrar, como podriamos ir indicando en nuestro codigo el progreso de nuestra tarea (como ejemplo pongo el metodo doGet de un Servlet aunque se podria hacer lo mismo en un doPost, el perform de Struts, etc...):
public void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
SessionProgressBox box = new SessionProgressBox(request.getSession(), "5");
System.out.println("¡ Hola Don Pepito !");
box.setProgress(10);
System.out.println("¡ Hola Don Jose !");
box.setProgress(20.5);
System.out.println("Pasaba por su casa.");
box.setProgress(40);
System.out.println("Por mi casa pasa usted.");
box.setProgress(60);
System.out.println("¡ Adios Don Pepito !");
box.setProgress(80.5);
System.out.println("¡ Adios Don Jose !");
box.setProgress(100);
}
|
Asi podeis ver como he marcado progresos del 10, 20.5, 40, 60, 80.5 y 100% en mi tarea "5".
Bueno, bueno, bueno... pues ya esta el plato listo y nos ha salido... ¡ riquisimo !. Para chuparnos los dedos.
Por supuesto, que esto son solo unas directrices, cada uno podeis y debeis cocinar como mas os guste. De hecho, este plato es una version muy simplificada de la que he cocinado en mi "Resturante". Ya sabeis que el objeto SessionProgressBox, o cualquier otra implementacion de la interfaz ProgressBox, podeis propagarla por toda vuestra aplicacion.
A veces podeis monitorizar un metodo que sea llamado por dos tareas y que en cada uno de ellas, la consecucion de un determinado hito suponga un porcentaje de progreso diferente... no problemo !!!. Para eso ProgressBox tiene el utilisimo metodo getTask() que te indica la tarea que se esta monitorizando. Asi podeis, por ejemplo asignar en un hito porcentajes de consecucion distintos para la tarea "pepino" y la tarea "melon":
if(box != null && "pepino".equals(box.getTask()) {
box.setProgress(15);
}
else if(box != null && "melon".equals(box.getTask()) {
box.setProgress(87.5);
}
|
Asi, habeis visto, pasando por el mismo hito le hemos asignado un 15% de progreso a la tarea "pepino" y un 87.5% a la tarea "melon". Tambien es importante que os deis cuenta que, ademas de averiguar en que tarea esta, se ha preguntado primero si el objeto ProgressBox es nulo esto es importante por lo mismo que hemos hablado antes, a lo mejor ese hito que esta dentro de un metodo, se llama desde un primer metodo que no monitoriza el progreso de ninguna tarea y pasa el objeto ProgressBox a nulo.
Bueno... pues ya esta, espero que, por la longitud de este post, no haya cometido muchos errores (mucho del codigo lo escribo de cabeza) y que NO TODOS OS HAYAIS DORMIDO. Si el post tiene aceptacion, poco a poco ire escribiendo mas clases de cocina. Ya sabeis...
RICO, RICO... Y CON FUNDAMENTO
(2004-10-11 14:13:31.0)
Permalink

Monday September 27, 2004
Probando JBoss 4.0 (3)
Antes de que Belial escribiese acerca de los problemas de carga de distintas versiones de librerias en JBoss, ya me habia enfrentado al problema, eso si, lo solucione de una manera mucho menos elegante que el.
Desplegada mi aplicacion, resulta que determinados procesos de la misma dejaban de funcionar. Al poco, descubri que el problema era la carga de versiones erroneas o antiguas de jars o librerias.
El error no era tanto que JBoss NO cargara la version de libreria que yo queria, sino incluso LA LIBRERIA que yo queria. Por ejemplo, como procesador XSLT, JBoss utiliza Xalan, sin embargo -y sin enumerar las mil y una razones- yo utilizo Saxon.
Otro problema grande lo tuve con la version de la libreria de Jakarta Avalon, que tambien tuve que cambiar para que mi aplicacion funcionara correctamente, en concreto, al generar PDFs.
Mientras que Belial lo ha solucionado como un campeon, mediante descriptores XML, yo lo solucione a la brute sustituyendo las librerias que intenta cargar JBoss. Asi, en el arranque default sustitui en el directorio /lib la version de Avalon cargada.
Ademas, en el directorio /lib/endorsed del servidor sustitui el .jar de Xalan por el de Saxon. Asi, conseguir que mi aplicacion funcionara adecuadamente.
Hasta que Belial nos ilustre un poco mas acerca de QUE descriptor y DONDE dentro del mismo, hay que tocar seguire con mi JBoss "tuneado". No obstante, es mucho mas recomendable, una vez que sepamos como, seguir su procedimiento antes que mio.
(2004-09-27 09:19:06.0)
Permalink

Friday September 24, 2004
Probando JBoss 4.0 (2)
Sigo probando JBoss. He intentado cambiar el puerto de escucha de JBoss, del 8080 que trae por defecto a un mas elegante 10000. Pues bien, podeis coger el parrafo de la documentacion que dice que hay que tocar el descriptor jboss-service.xml y tirarlo a la basura.
Para cambiar el puerto de escucha a JBoss hay que tocar el descriptor server.xml que encontrareis dentro del TOMCAT embebido en el arranque default. Este TOMCAT esta embebido como una aplicacion mas (jbossweb-tomcat50.sar) dentro del directorio de despliegue (/deploy) y hace de servidor web.
Pues eso, os meteis ahi, cambiais el puerto del 8080 al que os guste y a tirar. Espero haberle ahorrado a mas de uno una perdida de tiempo.
(2004-09-24 12:52:53.0)
Permalink

Wednesday September 22, 2004
Probando JBoss 4.0 (1)
Como ya nos dijo jerolba, JBoss ha lanzado la version 4 de su servidor de aplicaciones.
Cual intrepido aventurero, me he dispuesto probar el servidor a fondo y empezar a utilizar exhaustivamente toda la fontaneria que ofrece. Lo primero que me encuentro es que la distribucion viene "pelada", algunos samples, algunos test pero documentacion, lo que se dice documentacion CERO PATATERO. Intento conseguir informacion en la web de JBoss, la documentacion buena, la buena de veras, la cobran, asi que me bajare el Getting Started que es bastante completito.
Sorpreson, para poder disfrutar de toda la documentacion, foros y demas, me tengo que hacer "Supermember Member", o algo asi. Me doy de alta, y descubro que no hay informacion especifica de la version 4.0, te tienes que bajar la info de la version 3.2.x que, probablemente, valga igual, pero te deja con un halo de inseguridad.
Una vez bajada la distribucion, intento arrancar el servidor en mi maquina (con Windows como entorno de trabajo) y... tachaaaaan, despues de darme con la cabeza contra la mesa 30 veces, descubro que no consigo ni arrancar el JBoss con el fichero run.bat, no porque sea un cenutrio integral (bueno, algo lo soy) sino porque a JBoss no le gustan las rutas con espacios.
No son muy buenos comienzos, pero, al menos, ya tengo instalado el servidor en mi maquina para empezar a destriparlo. Nuevas impresiones en sucesivas entregas.
(2004-09-22 16:36:43.0)
Permalink

Wednesday September 08, 2004
Real Developers: Juan Carlos Saez
Siguiendo con la serie (un poco abandonada pero no olvidada) de Real Developers hoy os quiero presentar a otro programador, Juan Carlos Saez. No es un guru ni un freaky de escasa vida sexual de esos que entrevistan en jH ni falta que le hace. Es uno de nosotros, un informatico español (como Gibraltar).
JC, presentate para la gente que no te conozca
Mi nombre es Juan Carlos Sáez. Tengo 34 años. Soy Diplomado en Informática por la Universidad Politécnica de Madrid. Me gusta viajar, levantarme tarde y la musica siniestra sueca.
Mmmm... 34 años !!! ¡ Estas hecho un abuelo !. ¿ Cual ha sido tu trayectoria laboral ?
Llevo 14 años en el mundo de la informática. La verdad es que esto ha cambiado mucho desde que yo empecé.
Mi trayectoria profesional ha estado siempre vinculada a las aplicaciones telemáticas, desde mis comienzos. Inicialmente en el mundo Ibertex, basado en la red telefónica y X.25, para posteriormente pasar el mundo Internet. La diferencia entre ambos mundos era abismal. Ibertex se basaba en una infraestructura de red de muy escaso ancho de banda en comparación con el ancho de banda que se maneja ahora, así como la potencia de las herramientas de desarrollo era muy diferente.
Siempre me he movido en el mundo Unix, principalmente AIX (IBM) y Solaris (Sun). He desarrollado con herramientas de generación de aplicaciones Ibertex, lenguaje C, shell-scripts, Clipper, JavaScript, Java, HTML, etc. Me he pegado bastante con las comunicaciones por línea serie, X.25, TCP/IP, etc.
Mi llegada al mundo Java y de los servidores de aplicaciones, sin embargo, es relativamente reciente. Para mí lo más difícil ha sido el cambio de mentalidad desde la programación tradicional a la programación orientada a objetos.
En cuando al aspecto sectorial, he trabajado para la administración local (información al ciudadano) y la banca (comercio electrónico) principalmente.
¿ Que ha sido lo mejor y lo peor que te ha pasado profesionalmente en todos estos años ?
Lo peor, vivir el cierre de una empresa y soportar una pésima dirección, con decisiones totalmente arbitrarias. Ver como las empresas dejan escapar excelentes profesionales por irrisorias cantidades de dinero.
Lo mejor, haber conocido profesionales de gran calidad técnica y humana con los que he compartido muy buenos momentos y de los que he aprendido mucho.
¿ Que tecnología te ha impactado mas de las que has utilizado ?
La tecnología que más me ha impactado ha sido Java y los servidores de aplicaciones, por el cambio de enfoque tan brutal respecto a las tecnologías anteriores.
¿ Que es lo que mas y lo que menos te gusta de JAVA ?
Lo que mas, La gran cantidad de APIs y herramientas de que dispone. Lo que menos, es un lenguaje muy orientado al diseño. A veces lo veo algo complicado para aplicaciones muy pequeñitas de esas de "aquí te pillo y aquí te mato".
Si, JAVA no es para niños ;) ¿ Donde te ves dentro de 10 años ?
Me veo pidiendo en el Metro (es broma). La verdad es que no lo sé. Por ahora, a pesar de llevar 14 años, me sigue gustando mucho "arremangarme" y desarrollar. Quizás dentro de unos años, cambien mis preferencias, pero creo que seguiré cerca de la técnica. Soy partidario de los técnicos "pata negra", es decir, "endurecidos" expertos en las más diversas lides, capaces de identificar y resolver un problema en tiempo récord. Creo que es un concepto clave para la productividad de las empresas y que, por desgracia., todavía hay muchas que no ven.
Si, Yo tambien creo en los programadores senior DE VERDAD (con 10 o 15
años de experiencia), pero, ya sabes que eso en España es Ciencia Ficcion. ¿ Crees que en España el empresariado informatico funciona con el "todo vale dame pasta hoy " ?
Creo que todo es un círculo. No sólo el empresario quiere la pasta ya. También el cliente quiere la aplicación ya. Llega el empresario, le vende la moto y "a correr". Por supuesto, se dejan de lado aspectos como la calidad de la aplicación. Lo más difícil es intentar prevenir al cliente de lo que le va a ocurrir en un futuro si se deja vender motos. También se echan en falta empresarios honestos que digan cosas como: "Si le han dicho que se lo hacen en dos meses están locos. Es imposible hacer algo de una mínima calidad en ese tiempo". Por otra parte pienso que el empresario español todavía presta más atención a lo material (hardware por ejemplo) que a lo intangible (el tiempo, por supuesto), con lo cual se produce un racaneo de medios que redunda en el rendimiento. Y para terminar, qué decir del "volvamos al siglo XIX y trabajemos 12 horas diarias" con el que se solventan muchas situaciones. Si un proyecto se tiene que sacar así es que algo falla: la planificación, la dirección, la capacitación técnica...
¿ Hacia donde te gustaría que se orientara tu trayectoria profesional ?
Mi orientación profesional me gustaría que se dirigiera hacia la arquitectura de aplicaciones, así como a temas de Ingeniería del Software y Calidad.
¿ Software y Calidad ? Espero que tengas suerte y acabes trabajando en una empresa cliente. No, ahora en serio, muchas gracias por esta pequeña y humilde entrevista. Te debo una.
(2004-09-08 15:30:58.0)
Permalink

Tuesday September 07, 2004
Codigos de Barras y Java
Existen muchos frameworks para la generacion de Codigos de Barras pero, de entre todos ellos, os quiero hablar de Barbecue que destaca por ser sencillo, elegante y, sobre todo, Open Source.
El funcionamiento es mas simple que el asa de un cubo. Le das los datos a codificar, el tipo de codificacion y el framework te genera una imagen con el codigo de barras. Esta imagen la puedes meter directamente en el response del flujo de tu aplicacion o guardarla en un fichero .jpg para utilizarlo posteriormente (por ejemplo, en mi caso para componer un PDF de una factura a traves de una transformacion XSLT con un XML de datos y un XSL).
Una recomendacion, utilizar los datos a codificar y el tipo de codificacion para componer el nombre de los archivos .jpg que vayais guardando asi, creareis una especie de "pseudocache". Antes de crear el codigo de barras preguntar si el fichero para esos datos y esa codificacion existe. Si es asi, lo recuperais y punto; si no existe lo creamos y lo guardamos.
Un ejemplo, si queremos el codigo de barras del numero 1234567890 codificado en EAN128, podeis guardar el fichero como EAN128_1234567890.jpg. Si alguna vez teneis que generar un codigo de barras identico, podeis buscarlo preguntando si el fichero existe (metodo exist() del objeto java.io.File) antes de intentar crearlo.
El unico pero que le pongo al framework es la escasa (o nula) informacion que hay en la pagina web para ayudarte a crear mas codificaciones (EAN13, por ejemplo) que las que ellos proporcionan por defecto. Eso si, te animan a que contribuyas encarecidamente ;) .
(2004-09-07 09:23:14.0)
Permalink

Friday June 18, 2004
Java y Crystal Reports
Las herramientas de Reporting pueden ahorrar mucho trabajo al programador de aplicaciones JAVA de entorno empresarial.
El diseño, creacion y gestion de informes produce un beneficio inmediato en todas las areas de la compañia, desde la fuerza de ventas hasta el director general y, aunque a veces se minusvalora, es uno de los pilares de la Arquitectura de Sistemas que debe ser definido con mayor cuidado.
En el mundo JAVA existen aproximaciones muy notables al mundo del reporting como, por ejemplo, Jasper Reports. Es este un framework estable, extendido y,sobre todo, gratuito.
Sin embargo, Jasper Reports y muchos otros no pueden ni soñar con el numero de opciones y complejidad de los informes generados con Crystal Reports.
Crystal Reports es la mejor herramienta de reporting, es el estandar de facto en el mundo empresarial y tiene una curva de aprendizaje asumible... el unico problema (simpre hay un problema) es que no es gratis.
En el pasado, Crystal Reports hizo unos timidos intento de entrar en el mundo de los sistemas distribuidos pero su integracion con JAVA era bastante inquietante. Todo esto ha cambiado con el lanzamiento de Crystal Enterprise.
Crystal Enterprise, ha grandes rasgos, es un servidor web, que funciona como repositorio de reports y, ademas, de muchas mas cosas como por ejemplo Business Views, unos objetos propietarios de Crystal que sirven para aglutinar y restringir distintas fuentes de datos (XML, EJBs, Tablas, etc...). Esto, es solo parte de todo lo que aporta Crystal Enterprise para arropar a los reports.
Excepto el hecho de que Crystal Enterprise pueda funcionar sobre un servidor web Java (Tomcat es el unico libre soportado), poco o nada hemos visto de la integracion de Crystal Reports con JAVA que, al fin y al cabo, es lo que nos importa. Esta integracion, se basa en un completo SDK con el que se puede trabajar directamente contra Crystal Enterprise, solicitar informes, mostrar estos como DHTML... o incluso pasar a los informes un ResultSet como parametro para que trabaje con los datos contenidos en el. Como veis, muy potente.
Ademas del completo SDK, JBuilder X posee un plugin para trabajar con Crystal Reports -que no con Crystal Enterprise- esto significa que, aunque da ayuda para trabajar con Reports, debe hacerlo de forma "directa" con los ficheros .rpt dentro del contexto de la aplicacion.
Como conclusion, si necesitamos Reporting de alto nivel en nuestras aplicaciones empresariales, Crystal Reports en conjuncion con Crystal Enterprise puede ser una buena opcion.
Si alguien tiene interes por conocer un poco mejor el mundo de los reports y su integracion de JAVA puede bajarse una version de evaluacion gratuita de la web de Bussiness Objects tanto de Crystal Reports como de Crystal Enterprise. Yo, desde luego, os animo a probarlo
(2004-06-18 13:57:22.0)
Permalink

Thursday May 13, 2004
PCL, PJL y Java: una solucion teorica
Aun seguimos evaluando soluciones a la Gestion de Impresion de nuestra aplicacion pero, por lo menos, ya hemos encontrado una solucion teorica de estilo I bake it I eat it (yo me lo guiso yo me lo como): mezclar PCL y PJL en el flujo de bytes que se manda a la impresora.
Como nunca tuve que trabajar de una manera tan exhaustiva la impresion de ninguno de mis proyectos, el PCL era y es un mundo ignoto para mi.
Leyendo las especificaciones de PCL (un lenguaje de marcas, ni mas ni menos) vi marcas que solucionaban casi cualquier requisito de imprewsion: copias multiples, orientacion, etc. pero, habia un requisito para el que no vi marca por ningun lado: el grapado. Me revise entera la especificacion y nada, no aparecia nada. Cuando ya desesperaba, se me ocurrio pensar en el PJL y ahi encontre mi solucion.
Mientras el PCL es un lenguaje meramente de composicion de documentos, el PJL sirve para trabajar con estos documentos. Asi, tu puedes mandar un flujo de datos a la impresora poniendo de cabecera tus comandos de PJL -incluidos los de grapado- y gestionar la impresion.
Como supongo que a nadie se le habra ocurrido la locura de componer PCL por si mismo, imaginemos que utilizamos XSL:FO para producir una salida de datos. En vez de utilizar el Render de PDF, utilizaremos el de PCL para obtener una salida en este formato. Bien, esta salida de FO es PCL5 y... funciona!, pero ademas del PCL necesitamos utilizar comandos PJL. Eso esta hecho.
Incrustamos una cabecera de este tipo en el flujo de datos:
%-12345X@PJL JOB NAME="Sin título - Bloc de notas"
@PJL COMMENT CANPJL SET USERNAME="Administrador"
@PJL COMMENT CANPJL SET MEDIATYPE=MEDTYPEPAPER
@PJL COMMENT CANPJL SET DEVICE=PRINTER
@PJL COMMENT CANPJL SET TONERREDUCTION=GENERICOFF
@PJL SET DUPLEX=OFF
@PJL COMMENT CANPJL SET SORTERMODE=GENERICON
@PJL COMMENT CANPJL SET BOOKLET=GENERICOFF
@PJL COMMENT CANPJL SET STAPLE=GENERICOFF
@PJL SET RESOLUTION=600
Y ya tenemos nuestros comandos PJL (en negrita el de grapado). Por supuesto, si le mandas un comando de grapado a una impresora que no contempla esa opcion, nadie sabe con seguridad que puede pasar pero, por logica, supongo que lo ignorara. Deberemos aislar los comandos genericos.
Una vez que hayamos incrustado los comandos PJL, escribiremos el siguiente comando
@PJL ENTER LANGUAGE=PCL
Y seguidamente el chorro de PCL que tenemos previamente reservado (como diria Arguiñano).
Parseando el PCL podriamos meter la logica que quisieramos (numero de copias configurables segun el contenido, etc.) desde nuestra aplicacion JAVA. Claro, esta es una solucion teorica porque solo de pensar lo que tardariamos en programar un coponente flexible y configurable que realizara este trabajo se me ponen los pelos como escarpias.
Hay productos muy buenos en el mercado, y soluciones de todo tipo. En especial os recomiendo que le echeis un vistazo a esta herramienta: FormScape v3 , muy buena, pero cara.
Os pongo links a las especificaciones de PCL5 y PJL de HP, por si alguno se anima a mirarselas:
Manuales
(2004-05-13 08:41:32.0)
Permalink

Monday May 10, 2004
PCL y Gestion de Impresion
Creo que la gestion de impresion en Java es un mundo olvidado y desconocido para muchos. Yo mismo, habiendo trabajado en proyectos grandes, nunca habia tenido que preocuparme por una gestion de impresion seria hasta que me hecho cargo de mi actual proyecto.
Muchos pensaran que como gestion de impresion se habla de la creacion de PDFs o del envio de documentos a la impresora pero, no es asi.
La gestion de impresion abarca mucho mas, comprende toda la logica que puede conllevar una impresion: desde indicar que un documento debe ir grapado hasta que las facturas de un determinado cliente tienen tres copias y las del resto solo 2. Todas estas operaciones, no tienen nada que ver con la obtencion de datos a imprimir, ni siquiera con la presentacion, sino unica y exclusivamente con la impresion de los mismos y, por lo tanto, en una arquitectura de capas, esta gestion deberia diseñarse como una capa mas independiente y autonoma de las demas.
Existen muchas soluciones en el mercado para gestionar la logica de impresion pero, despues de evaluar una amplia gama de productos, no he encontrado nada hecho en JAVA. ¿ Estamos ante un territorio virgen ?.
Lo mas aproximado que he encontrado es un visor de PCL hecho en JAVA pero este visor -aunque te permite hacer cambios de presentacion- es un mero visor-editor, no te permite meter una capa de logica entre la impresion y tus datos.
Muchos pensaran que esta logica de impresion y el trabajo con PCL puro y duro no son tecnologias maduras en el mundo JAVA por la especial naturaleza multiplataforma del lenguaje pero, si PCL se ha convertido en un standard, JAVA deberia ser capaz de trabajar con el.
Visto lo visto, evaluamos dos posibilidades a la hora de solucionar la gestion de impresion:
- Utilizar un producto comprado hecho en Visual Basic que, por lo menos, consume XML, aunque eso supondria tener que introducir algunas marcas de logica de impresion en el XML que generaramos (habria que generar XML especifico para la impresion, no valdria el standard que ya produce la aplicacion).
- Trabajar con el PCL nosotros mismos. Si, es inevitable hacer un trabajo especifico para la logica de impresion, ya lo hacemos del todo y no compramos ningun producto. El unico problema que nos hemos encontrado para lanzarnos a esta aventura es que no hemos encontrado manera de indicarle a la impresora que grape en el manual de PCL5 (el que genera XSL:FO) que hemos conseguido.
Aun estamos decidiendo que solucion adoptar pero ya hemos podido extraer algunas conclusiones:
- JAVA no tiene muchas APIs de apoyo a la impresion.
- JavaPrinting esta orientada a la impresion de graficos.
- Es muy dificil obtener documentacion y manuales sobre PCL incluso en la web de desarrolladores de HP.
Seguiremos informando ...
(2004-05-10 08:45:14.0)
Permalink

Sunday April 11, 2004
Borland Enterprise Studio for Java 7
¿ Alguien lo ha visto ? ¿ Alguien lo ha probado ?. (Aqui teneis una captura de pantalla)
Ya se que por aqui hay mucho fanatico de Eclipse pero, la verdad, pensar en un herramienton que una el codificador de JBuilder X y la capacidad de diseño de Together 7 hace que empiece a salivar ... .
Lo malo no es que sea de pago sino que no te lo puedes descargar directamente de la web de Borland. Siendo solidario con la compañia, he intentado buscarlo por otros mundos (eMule, warez, etc.), mas que nada para ahorrar a Borland ancho de banda ;) y nada ... parece que el Enterprise Studio no existe.
Un amigo mio que trabaja en Indra ya lo ha podido disfrutar. De algo tenia que servir trabajar en una de esas compañias megalomanas que ansian secretamente dominar el mundo.
(2004-04-11 13:00:44.0)
Permalink

Tuesday April 06, 2004
El proyecto bipolar: ¿ Una quimera o una genialidad ?
Es duro migrar una aplicacion hecha en Informix a J2EE y mas aun cuando pillas el desarrollo a la mitad, con miles de lineas de codigo escritas y un retraso heredado de un año.
Hay muchas cosas que me hubiera gustado hacer de otra manera pero con las que tengo que seguir hasta la muerte... o hasta que acabe el proyecto.
El problema que mas me asfixia a mi y a mi equipo de desarrollo es el tiempo... bueno, el tiempo y el hecho de no poder ir poniendo modulos en produccion sino tener que hacerlo de golpe y es que, al haberse concebido la aplicacion de manera monolitica y orientada a una base de datos en concreto, lo que no se puede hacer es que la gente digite facturas y se guarden en Oracle mientras el Jefe de Cobros trabaja con las que hay en Informix.
Podeis pensar que todo es tan sencillo como usar el driver JDBC adecuada para cada Base de Datos y tirar para adelante pero, no es asi.
LOS DOS MODELOS DE DATOS SON DIFERENTES... empezando porque el modelo de Oracle tiene integridad referencial y el de informix no y acabando en... muchas cosas.
Si pudiera empezar a subir modulos a produccion empezariamos a producir algo de ROI (Return of Investment) e iriamos mas relajados para terminar los que aun quedan, el problema...
¿ Como conseguir integridad y estabilidad en los datos ? Las cosas deberian grabarse en ambas bases de datos... ¿¿¿¿¿ y consultarse en ambas base de datos ?????. ¿ Como se podria trabajar con dos modelos distintos haciendo codigo JAVA compatible con ambos ? Con DAOs si... pero que DAOs !!!.
Supongo que todo esto es una entelequia, un desvario. Quizas esto hubiera sido posible con una buena planificacion y diseño desde el principio pero ahora, posiblemente, el coste de programacion sea enorme.
Moraleja, piensa muy bien la subida a produccion cuando afrontes un proyecto de gran envergadura.
(2004-04-06 19:49:01.0)
Permalink

Thursday April 01, 2004
Java, Memoria y Recursion: el parametro Xss
Parece que hay una pequeña confusion respecto a la verdadera funcionalidad de los parametros de control de memoria que se proporcionan en el arranque de una aplicacion JAVA para realizar un pequeño tunning que mejore el rendimiento de la misma.
Es conveniente recordar que los parametros Xmx y Xms indican la cantidad maxima y minima de memoria que utiliza el proceso JAVA y nada mas que eso. Establecemos los limites del espacio de memoria que queremos reservar para dicho proceso.
El parametro Xss sirve para aumentar el tamaño de la pila, lo cual, para alguien que no es un experto en el diseño interno de la maquina virtual -como yo- no significa mucho. Lo que si os puedo contar es cuando lo he utilizado como lo he utilizado y para que.
Vereis, he programado una herramienta de generacion de informes que consume XML y lo procesa con XSLT para producir distintas salidas. El caso es que, ademas, se ha programado un modulo que genere el XML validado segun el Schema que espera esta herramienta a partir de un Array de objetos JAVA y que se pueda programar el informe deseado a partir de otro XML de configuracion.
Hablando claro: dame un array de objetos JAVA, configurame en un XML que tipo de objetos me mandas y que quieres que haga con ellos y te saco un PDF o un HTML con tu informe. Por ejemplo, el tipo objeto que corresponde a un CLIENTE; a la aplicacion le mandas un ArrayList de objetos ViewCliente y le dices que presente el informe cortado por Pais y, en un segundo corte jerarquico, por provincia.
Para poder hacer todo esto, y en caliente -es decir, se puede programar en tiempo de ejecucion, sin parar el servidor- utilizamos el ya nombrado XSLT y, la reflexion y hay es donde esta el problema.
Si se produce reflexion a un nivel muy grande, por ejemplo, 16.000 reflexiones seguidas, sencillamente JAVA muere. Me costo localizar el Error y cuando digo Error es eso, literalmente, porque no es una Exception:
StackOutOfMemoryError
... y la JVM se queda tan ancha y tan pancha. Bien, este problemon se soluciono dandole un tamaño a la pila de 10M (-Xss10M)... y a funcionar.
Tambien os podria decir que estos problemas "solo" aparecen sobre un JBoss con Xalan como XSL Engine mientras que un Tomcat con Saxon (programado por mi amigo el calvorota Michael Kay) esto no ocurre, pero bueno... esto daria para otro post.
(2004-04-01 08:22:41.0)
Permalink
|
|
| Archivos |
|
|
| « October 2008 | | Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|
| | | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | | | | | | | | | | | Hoy |
|
|
|
| Busca en el weblog |
|
|
|
|
|
|
| Navegacion |
|
|
|
|
|
|
|
| De donde venis |
|
|
|
Las visitas de hoy a la página: 58
|
|
|
|
|
|