viernes, 31 de diciembre de 2010

Struts 1X: Cómo subir un archivo ("uploading")?

Dejo como referencia este código para poder crear un formulario en cual podamos subir (aka "upload") un archivo con Struts 1X.

Código JSP:
<html:form action="/snapshotUploadAction.do" method="post" enctype="multipart/form-data">
File : <html:file property="formFile" /> <html:submit />
</html:form>

<c:if test="${not empty fileUploadForm.message}">
<p style="color:red;">${fileUploadForm.message}</p>
</c:if>


Código del ActionForm:


import org.apache.struts.action.ActionForm;
import org.apache.struts.upload.FormFile;

public class FileUploadForm extends ActionForm {

private static final long serialVersionUID = 1L;

protected FormFile formFile;
protected String message;

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public FormFile getFormFile() {
return formFile;
}

public void setFormFile(FormFile formFile) {
this.formFile = formFile;
}
}


Código del Action:


import java.io.File;
import java.io.FileOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.upload.FormFile;
import com.eol.core.controller.form.FileUploadForm;

/**
* Basic action to be used to upload files.
* @author gsolano
*
*/
public class FileUploadAction extends Action{

@Override
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {

FileUploadForm uploadForm = (FileUploadForm) form;
FileOutputStream outputStream = null;
FormFile formFile = null;
String path="";
formFile = uploadForm.getFormFile();

if(formFile != null) {
try {
path = getRepositoryPath() + formFile.getFileName();
System.out.println("File will be uploaded to this path: " + path);
outputStream = new FileOutputStream(new File(path));
outputStream.write(formFile.getFileData());

} catch(Exception exception) {
System.out.println(exception);
}
finally {
if (outputStream != null) {
outputStream.close();
}
}
/* Do something with the just uploaded file*/
processUploadedFile(path);
uploadForm.setMessage("The file " + formFile.getFileName() + " was uploaded successfully.");
}
return mapping.findForward("success");
}

/**
*
* @param path
*/
protected void processUploadedFile(String path) {
/*MUST OVERRIDE TO DO SOMETHING WITH THE UPLOADED FILE*/
}

/**
* This path where the file will be uploaded is put in a method to allow
* classes extending to be able to override it.
* @return
*/
protected String getRepositoryPath() {
return getServlet().getServletContext().getRealPath("")+"/";
}
}


Resultado:

miércoles, 8 de diciembre de 2010

Joomla: Insertar código HTML personalizado en artículo

Uno esperaría que esto fuera una opción bastante sencilla y directa. En efecto cuando uno abre el editor encuentra el botón HTML para insertar contenido en dicho lenguage.


Necesitaba colocar un mapa de Google empotrado en la página insertando el código que Google provee. Lo inserté y guardé pero luego me di cuenta que el código original:


<iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://maps.google.com/maps/ms?ie=UTF8&hl=es&t=h&msa=0&msid=117809438193952087338.0004964e1a9e9e1fcf96f&ll=9.938713,-84.04923&spn=0.007397,0.00912&z=16&output=embed"></iframe><br /><small>Ver <a href="http://maps.google.com/maps/ms?ie=UTF8&hl=es&t=h&msa=0&msid=117809438193952087338.0004964e1a9e9e1fcf96f&ll=9.938713,-84.04923&spn=0.007397,0.00912&z=16&source=embed" style="color:#0000FF;text-align:left">MET - UCR</a> en un mapa más grande</small>


había cambiado:



Parece que el editor hace ciertas transformaciones al código HTML que impiden, en este caso particular, poder usar el código del mapa de Google. Investigando un poco en Internet, en un sitio daban un tip que consiste en crear un super usuario "noeditor" seleccionando la opción de no usar ningún editor.



Ingrese a Joomla con este usuario, edite el código y de esta manera pude insertar el código HTML sin que Joomla lo modificara. Uno podría también editar las opciones de su propio usuario, pero resulta más práctico tener un usuario asignado para este tipo de ocasiones.

jueves, 2 de diciembre de 2010

Diferencia entre Clase Abstracta e Interface


Potencial pregunta de entrevista de trabajo. La diferencia puede parecer obvia en el código, pero en términos de funcionalidad existe un traslapo donde tenemos que detenernos por un momento a reflexionar, si es que no lo hemos hecho, por qué debemos usar una opción o la otra.

Lo primero que recordamos de una clase abstracta es que no podemos instanciarla. La clase abstracta se usa como interfaz de las subsecuentes clases que harán conversión hacia arriba ("upcasting"). Podemos describir el upcasting como la acción de declarar una variable de una clase base (abstracta en la mayoría de los casos), pero instanciando una implementación de la misma. Esto da vida al comportamiento polifórmico.

MyAbstract myAbstract = new MyImplementation();

Veamos un ejemplo de una clase abstracta reloj.


public abstract class Watch {

/* Instance variables */

protected int seconds;
protected int minutes;
protected int hours;

/* Constructors */

public Watch() {

}

public Watch(int h, int m, int s) {
hours = h;
minutes = m;
seconds = s;
}

// Must override this method.
abstract public void showTime();

/*Getters and setters for the time components.*/

public int getSeconds() {
return seconds;
}

public void setSeconds(int seconds) {
this.seconds = seconds;
}

public int getMinutes() {
return minutes;
}

public void setMinutes(int minutes) {
this.minutes = minutes;
}

public int getHours() {
return hours;
}

public void setHours(int hours) {
this.hours = hours;
}
}


Y ahora la implementación de un reloj dijital.


public class DigitalWatch extends Watch {

public DigitalWatch(int h, int m, int s) {
super(h,m,s);
}

public void showTime() {
System.out.println(hours +":" + minutes + ":" + seconds);
}

public static void main(String[] args) {
Watch myWatch = new DigitalWatch(5, 30, 20);
myWatch.showTime(); // Prints "5:30:20".
}
}


Si intentamos replicar este diseño con el uso de una interfaz vemos que necesitamos mover el código antes localizado en la clase base Watch en la implementación de DigitalWatch.


public interface WatchI {

public void showTime();

}


public class DigitalWatchI implements WatchI{

protected int seconds;
protected int minutes;
protected int hours;

public void showTime() {
System.out.println(hours +":" + minutes + ":" + seconds);
}

public DigitalWatchI(int h, int m, int s) {
hours = h;
minutes = m;
seconds = s;
}

/*Getters and setters for the time components.*/

public int getSeconds() {
return seconds;
}

public void setSeconds(int seconds) {
this.seconds = seconds;
}

public int getMinutes() {
return minutes;
}

public void setMinutes(int minutes) {
this.minutes = minutes;
}

public int getHours() {
return hours;
}

public void setHours(int hours) {
this.hours = hours;
}

public static void main(String[] args) {
WatchI myWatch = new DigitalWatchI(5, 30, 20);
myWatch.showTime(); // Prints "5:30:20".
}
}


Las interfaces en Java se utilizan primordialmente para separar la interfaz de la implementación en una manera más estricta de como lo hace las clases abstractas. Una interfaz no permite del todo algún nivel de implementación en los métodos. Con la clase abstracta si podemos heredar la implementación de una clase a otra.

En resumen:

Classe Abstracta Interface
Contiene tanto métodos ejecutables como métodos
abstractos.
No tiene código de implementación.
Todos sus métodos son abstractos.
Una clase solo puede extender de una única clase abstracta. Una clase puede implementar n número de interfaces.
Puede tener variables de instancia, constructores y cualquiera de los tipos de visibildiad: public, private, protected, ninguno (aka package). No puede tener variables de instancia o constructores y solo puede tener métodos públicos o package.

jueves, 25 de noviembre de 2010

ArrayList versus LinkedList

Tenía pendiente asimilar un conocimiento básico y esencial para la programación sana siempre orientada al rendimiento. Usualmente cuando ocupo una lista en el código que trabajo siempre utilizo un "ArrayList" y la verdad tenía en la caja del olvido la implementación de "LinkedList". ¿Cuáles son las principales diferencias entre estas dos implementaciones?

Ventajas del ArrayList

1. El ArrayList utiliza internamente un arreglo para el almacenamiento. Esto lo hace particularmente más rápido para los accesos aleatorios tipo "obtener(#n)".

Desventajas del ArrayList

1. El ArrayList es más lento para agregar/borrar elementos en el inicio o en el medio de la colección debido a que todos los elementos (localizados antes o después) deben ser copiados adelante o atrás.

Grafiquémolo de esta manera. Digamos que tengamos esta lista con 6 elementos.


Y queremos insertar otro elemento antes del segundo.


Para poder agregar ese elemento, internamente la lista tiene que copiar todos los elementos del índice 1 a 5 una posición adelante.

2. Similar al problema anterior, el ArrayList tiene problemas de rendimiento cuando el arreglo interno se llena ya que tiene que crear uno arreglo más grande y copiar todos los elementos del arreglo viejo al nuevo.

Ventajas del LinkedList

1. La LinkedList (o lista enlazada) es lo opuesto a el ArrayList. Es más eficiente para agregar/borrar elementos en el medio o al comienzo de la colección. Si alguna vez les tocó programar a pie una estructura de datos de lista enlazada, recordarán que en la clase nodo se guarda un puntero o referencia al siguiente elemento.




En este caso lo que se hace para insertar un nodo en el medio es crea un nuevo nodo apuntando al elemento de adelante (->c) y se actualiza el puntero/referencia del nodo anterior para que apunte al nuevo nodo (->b).





2. Como no tiene restricciones de tamaño, la LinkedList no tiene los problemas de crecimiento del ArrayList.

Desventajas del LinkedList

1. Los accesos aleatorios son costosos en un LinkedList ya que en el peor de los casos tiene que recorrer toda la lista para llegar al elemento solicitado.

En resumen podemos decir que deberíamos utilizar el ArrayList solo si sabemos que vamos a tener muchos accesos aleatorios, en caso contrario usamos el LinkedList por defecto.

miércoles, 17 de noviembre de 2010

Joomla | Templates | jdoc:inculde tipo módulo

Finalmente pude descifrar la funcionalidad de la etiqueta jdoc:inculde en una plantilla de joomla, específicamente con el tipo módulo. Parece dificil de creer, pero tenia buen rato escarbando en Joomla para descubrir de dónde provenía el contenido que se estaba desplegando en el encabezado de arriba:


<div id="header_r">
<div id="logo"></div>
<jdoc:include type="modules" name="top" />
</div>


Resulta que debía ir a la parte de módulos y buscar por posición que en este caso es "top" (arriba).



El módulo asignado a esa posición es "Newsflash" que se alimenta de un feed de Joomla. Al final simplemente lo deshabilite usando el icono que aparece en la columna "Enabled".

martes, 2 de noviembre de 2010

Catalogo de tablas



Quisiera hacer eco de un artículo que me encontré buscando algunos estilos para aplicar a una tabla HTML. Mi intención no es hacer plagio del artículo sino el de dejar disponible de una forma más clara los CSS que utilizó en cada una de las tablas.

Ciertamente el estilo por defecto de una tabla HTML es demasiado crudo; muy feo se ve en mi opinión. Aún cuando agregamos algunos detalles básicos como bordes, las tablas que usualmento le aplico mi estilo terminan siendo aún menos vistosas.

Si descubro más estilos llamativos los anexaré a esta entrada de blog.

0. Default

DECOCTHEXBINSymbolHTML NumberHTML NameDescription
4806030001100000&#48;-cero
4906131001100011&#49;-uno
5006232001100102&#50;-dos
5106333001100113&#51;-tres
5206434001101004&#52;-cuatro
5306535001101015&#53;-cinco
5406636001101106&#54;-seis
5506737001101117&#55;-siete
5607038001110008&#56;-ocho
5707139001110019&#57;-nueve

1. Minimalista Horizontal

DECOCTHEXBINSymbolHTML NumberHTML NameDescription
4806030001100000&#48;-cero
4906131001100011&#49;-uno
5006232001100102&#50;-dos
5106333001100113&#51;-tres
5206434001101004&#52;-cuatro
5306535001101015&#53;-cinco
5406636001101106&#54;-seis
5506737001101117&#55;-siete
5607038001110008&#56;-ocho
5707139001110019&#57;-nueve

  
#minimalista-horizontal {
background:none repeat scroll 0 0 #FFFFFF;
border-collapse:collapse;
font-family:"Lucida Sans Unicode","Lucida Grande",Sans-Serif;
font-size:12px;
margin:20px;
text-align:left;
width:480px;
}
#minimalista-horizontal th {
border-bottom:2px solid #6678B1;
color:#003399; font-size:14px;
font-weight:normal;
padding:10px 8px;
}
#minimalista-horizontal td {
color:#666699;
padding:9px 8px 0;
}

2. Minimalista Vertical

DECOCTHEXBINSymbolHTML NumberHTML NameDescription
4806030001100000&#48;-cero
4906131001100011&#49;-uno
5006232001100102&#50;-dos
5106333001100113&#51;-tres
5206434001101004&#52;-cuatro
5306535001101015&#53;-cinco
5406636001101106&#54;-seis
5506737001101117&#55;-siete
5607038001110008&#56;-ocho
5707139001110019&#57;-nueve


#minimalista-vertical {
border-collapse:collapse;
font-family:"Lucida Sans Unicode","Lucida Grande",Sans-Serif;
font-size:12px; margin:30px 30px 30px 15px;
text-align:left; width:480px;
}
#minimalista-vertical th {
border-bottom:2px solid #6678B1;
border-left:30px solid #FFFFFF;
border-right:30px solid #FFFFFF;
color:#003399;
font-size:14px;
font-weight:normal;
padding:8px 2px;
}
#minimalista-vertical td {
border-left:30px solid #FFFFFF;
border-right:30px solid #FFFFFF;
color:#666699; padding:12px 2px 0;
}

3. Caja

DECOCTHEXBINSymbolHTML NumberHTML NameDescription
4806030001100000&#48;-cero
4906131001100011&#49;-uno
5006232001100102&#50;-dos
5106333001100113&#51;-tres
5206434001101004&#52;-cuatro
5306535001101015&#53;-cinco
5406636001101106&#54;-seis
5506737001101117&#55;-siete
5607038001110008&#56;-ocho
5707139001110019&#57;-nueve

  
#caja {
border-collapse:collapse;
font-family:"Lucida Sans Unicode","Lucida Grande",Sans-Serif;
font-size:12px; margin:20px;
text-align:left; width:480px;
}
#caja th {
background:none repeat scroll 0 0 #B9C9FE;
border-bottom:1px solid #FFFFFF;
border-top:4px solid #AABCFE;
color:#003399;
font-size:13px;
font-weight:normal;
padding:8px;
}
#caja td {
background:none repeat scroll 0 0 #E8EDFF;
border-bottom:1px solid #FFFFFF;
border-top:1px solid transparent;
color:#666699;
padding:8px;
}

4. Zebra Horizontal

DECOCTHEXBINSymbolHTML NumberHTML NameDescription
4806030001100000&#48;-cero
4906131001100011&#49;-uno
5006232001100102&#50;-dos
5106333001100113&#51;-tres
5206434001101004&#52;-cuatro
5306535001101015&#53;-cinco
5406636001101106&#54;-seis
5506737001101117&#55;-siete
5607038001110008&#56;-ocho
5707139001110019&#57;-nueve

  
#zebra-hor {
border-collapse:collapse;
font-family:"Lucida Sans Unicode","Lucida Grande",Sans- Serif;
font-size:12px;
margin:20px;
text-align:left;
width:480px;
}
#zebra-hor th {
color:#003399;
font-size:14px;
font-weight:normal;
padding:10px 8px;
}
#zebra-hor .odd {
background:none repeat scroll 0 0 #E8EDFF;
}
#zebra-hor td {
color:#666699;
padding:8px;
}

5. Zebra Vertical

DECOCTHEXBINSymbolHTML NumberHTML NameDescription
4806030001100000&#48;-cero
4906131001100011&#49;-uno
5006232001100102&#50;-dos
5106333001100113&#51;-tres
5206434001101004&#52;-cuatro
5306535001101015&#53;-cinco
5406636001101106&#54;-seis
5506737001101117&#55;-siete
5607038001110008&#56;-ocho
5707139001110019&#57;-nueve


#zebra-ver {
border-collapse:collapse;
font-family:"Lucida Sans Unicode","Lucida Grande",Sans-Serif;
font-size:12px;
margin:0 20px 20px;
text-align:left;
width:480px;
}
.zebrav-odd {
background:none repeat scroll 0 0 #EFF2FF;
}
.zebrav-even {
background:none repeat scroll 0 0 #E8EDFF;
}
#zebra-ver th {
border-left:1px solid #FFFFFF;
border-right:1px solid #FFFFFF;
color:#003399;
font-size:14px;
font-weight:normal;
padding:12px 15px;
}
#zebra-ver td {
border-left:1px solid #FFFFFF;
border-right:1px solid #FFFFFF;
color:#666699;
padding:8px 15px;
}

6. Enfasis en una Columna

DECOCTHEXBINSymbolHTML NumberHTML NameDescription
4806030001100000&#48;-cero
4906131001100011&#49;-uno
5006232001100102&#50;-dos
5106333001100113&#51;-tres
5206434001101004&#52;-cuatro
5306535001101015&#53;-cinco
5406636001101106&#54;-seis
5506737001101117&#55;-siete
5607038001110008&#56;-ocho
5707139001110019&#57;-nueve


#una-col {
border-collapse:collapse;
font-family:"Lucida Sans Unicode","Lucida Grande",Sans-Serif;
font-size:12px;
margin:20px;
text-align:left;
width:480px;
}
#una-col th {
color:#003399;
font-size:14px;
font-weight:normal;
padding:12px 15px;
}
#una-col td {
border-top:1px solid #E8EDFF;
color:#666699;
padding:10px 15px;
}
.una-col-primero {
background:none repeat scroll 0 0 #D0DAFD;
border-left:10px solid transparent;
border-right:10px solid transparent;
}

7. Periódico


DECOCTHEXBINSymbolHTML NumberHTML NameDescription
4806030001100000&#48;-cero
4906131001100011&#49;-uno
5006232001100102&#50;-dos
5106333001100113&#51;-tres
5206434001101004&#52;-cuatro
5306535001101015&#53;-cinco
5406636001101106&#54;-seis
5506737001101117&#55;-siete
5607038001110008&#56;-ocho
5707139001110019&#57;-nueve

 
#periodico {
border:1px solid #6699CC;
border-collapse:collapse;
font-family:"Lucida Sans Unicode","Lucida Grande",Sans-Serif;
font-size:12px;
margin:25px;
text-align:left;
width:480px;
}
#periodico th {
border-bottom:1px dashed #6699CC;
color:#003399;
font-size:14px;
font-weight:normal;
padding:12px 17px;
}
#periodico td {
color:#666699;
padding:7px 17px;
}
#periodico tbody tr:hover td {
background:none repeat scroll 0 0 #D0DAFD;
color:#333399;
}

8. Esquinas Redondeadas

DECOCTHEXBINSymbolHTML NumberHTML NameDescription
4806030001100000&#48;-cero
4906131001100011&#49;-uno
5006232001100102&#50;-dos
5106333001100113&#51;-tres
5206434001101004&#52;-cuatro
5306535001101015&#53;-cinco
5406636001101106&#54;-seis
5506737001101117&#55;-siete
5607038001110008&#56;-ocho
5707139001110019&#57;-nueve

  
#esqui-red{
border-collapse:collapse;
font-family:"Lucida Sans Unicode","Lucida Grande",Sans-Serif;
font-size:12px;
margin:20px;
text-align:left;
width:480px;
}
#esqui-red th {
background:none repeat scroll 0 0 #B9C9FE;
color:#003399;
font-size:13px;
font-weight:normal;
padding:8px;
}
#esqui-red td {
background:none repeat scroll 0 0 #E8EDFF;
border-top:1px solid #FFFFFF;
color:#666699;
padding:8px;
}
#esqui-red thead th.izq-sup-red{
background:url("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOmd9-h2iL32mVV54AeJBKAST4RmkCC5nfTPsUgTY0RkYnon4Db9_GNobIbaLIJrJFZV8-hp_mI8o_EqnN2jORB498MTOBIez_fPjG_XhhPuO0IqQzWfgF5T4SJR1u13__yVmRwq5ddinn/s800/izq.png") no-repeat scroll left -1px #B9C9FE;
}
#esqui-red thead th.der-sup-red{
background:url("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhX2suMO0KnpgjB7V8hlmDHp7W0o1Pl8UejVqzAWAOHHz61s3lfRaU7WV5X9ZkdT3YTa1Ke_XcGRJ__WzmGhGHMlD0KpZVme3MHN3f8GPd-_zDSW__5pTAtbzJUiW_tk5oXX5aMxlziv403/s800/der.png") no-repeat scroll right -1px #B9C9FE
}
#esqui-red td.pie-izq-red{
background:url("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEip4X_LCHnrSPDhS0RSjHGkl1E9JlpgJCq2VHVHK7W5DtaL6JU3smT2lkVFY1oCM7klASCUZvtMeGyWrBb9P-H2z82VmS_j2CPX3zlUIrYQc0oJqQSdKnDIk3KIgnLw6ye1pnY6Gu6_Zyy3/s800/abaj-izq.png") no-repeat scroll left bottom #E8EDFF;
}
#esqui-red td.pie-der-red{
background:url("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvwUrfVgG1h0O_KexURGkf8YJKpt4J-5OPxa9ZIrPrRLDb7dJYubhIX6RqJ4zVW0sYy08aur8H1KIlMUmVEKuZiDZsqbhPSF8HDF9xMAjlutXL9MRPqKCyktwoSyvsZ901Jhnuq-StqsKb/s800/abaj-der.png") no-repeat scroll right bottom #E8EDFF;
}

9. Fondo

DECOCTHEXBINSymbolHTML NumberHTML NameDescription
4806030001100000&#48;-cero
4906131001100011&#49;-uno
5006232001100102&#50;-dos
5106333001100113&#51;-tres
5206434001101004&#52;-cuatro
5306535001101015&#53;-cinco
5406636001101106&#54;-seis
5506737001101117&#55;-siete
5607038001110008&#56;-ocho
5707139001110019&#57;-nueve


#fondo-img {
border-collapse:collapse;
font-family:"Lucida Sans Unicode","Lucida Grande",Sans-Serif;
font-size:12px;
margin:20px;
text-align:left;
width:480px;
}

#fondo-img tbody {
background:url("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyprXjlzDAUg6BxomURrsMN6A0C2_ETlkTIQIzphkLti2-HBynIau0IWOgUV3vsvKAsPFtRIMbPwURxYd-R_LmXZmYZThVkb3HJ2OAGtV5JMctiNMyCDEEmOj15NpZWw9Y7sH_WLhXh2wb/s144/blurry.jpg") no-repeat scroll 330px 59px transparent;
}
#fondo-img th {
color:#333399;
font-size:14px;
font-weight:normal;
padding:12px;
}
#fondo-img td {
background:url("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3_iSAWJdnL2XRTZMW-ICJR2c6hksYsR7xrbghCliBd1on0mnmSbJFDEnmTjVNZ8iod4tyVZMKLclAM85Ry5OZmQWVs1-XgXqvBpX36T1dGyFqbKNRwmuh1WA6kk9X6Sxbi_j0uFcs-BWm/s144/back.png") repeat scroll 0 0 transparent;
border-top:1px solid #FFFFFF;
color:#666699;
padding:9px 12px;
}
#fondo-img tr:hover td {
background:none repeat scroll 0 0 transparent;
color:#333399;
}

10. Fondo Celda /Gradiente

DECOCTHEXBINSymbolHTML NumberHTML NameDescription
4806030001100000&#48;-cero
4906131001100011&#49;-uno
5006232001100102&#50;-dos
5106333001100113&#51;-tres
5206434001101004&#52;-cuatro
5306535001101015&#53;-cinco
5406636001101106&#54;-seis
5506737001101117&#55;-siete
5607038001110008&#56;-ocho
5707139001110019&#57;-nueve

 
#fondo-celda-a {
border-collapse:collapse;
font-family:"Lucida Sans Unicode","Lucida Grande",Sans-Serif;
font-size:12px;
margin:20px;
text-align:left;
width:480px;
}
#fondo-celda-a th {
background:url("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUuEgtmepzBtvhJRVFokZUVN-n6T63SQDZQf57xMe1g9o0_ttPbfjiwqn4epR6ZRtB6mm6CXkuJDsoWh7bi0lY6ZgR5dMGR4JMHYWF5aAncimCTyXfW9Rgnc8SLkqmrzWFX-yEikaK7-f_/s800/fondo-celda-encabezado-a.png") repeat-x scroll 0 0 #B9C9FE;
border-bottom:1px solid #FFFFFF;
border-top:2px solid #D3DDFF;
color:#003399; font-size:13px;
font-weight:normal;
padding:8px;
}
#fondo-celda-a td {
background:url("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPG-_PpA8vEGSCYkyNamO1qEfQonemzLRHfgScSdQUKlwwTsOZTq-q2TPSbBgro6-6lyzBkAyA1lvt76M3VoW-hGDjQR9sCMJk5HyfssHcWt9LxkTGCOtsz2pqLTlokF2ieyrQYgm8aJJV/s800/fondo-celda-cuerpo-a.png") repeat-x scroll 0 0 #E8EDFF;
border-bottom:1px solid #FFFFFF;
border-top:1px solid #FFFFFF;
color:#666699; padding:8px;
}
#fondo-celda-a tr:hover td {
background:url("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhppsNKUORN9SFfbGfLWk7OYl860P4sHH5b9TTofbpkScBd0rHQcEMt-pLDNjZ14vukPjxJxglYIH05HTZ8LxdFsqlmaR7Cw3AaKENistMZmSpxVT4rkPWR1bfHrOsoMI-LdkLX85gYZumR/s800/fondo-celda-cuerpo-hover-a.png") repeat-x scroll 0 0 #D0DAFD;
color:#333399;
}

11. Fondo Celda /Rayas

DECOCTHEXBINSymbolHTML NumberHTML NameDescription
4806030001100000&#48;-cero
4906131001100011&#49;-uno
5006232001100102&#50;-dos
5106333001100113&#51;-tres
5206434001101004&#52;-cuatro
5306535001101015&#53;-cinco
5406636001101106&#54;-seis
5506737001101117&#55;-siete
5607038001110008&#56;-ocho
5707139001110019&#57;-nueve
 
#fondo-celda-b {
background:url("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj83uee8BOyGztyw8lIqLuA-QBTD6wmoePe9HaVUYAiaMFM8QdqFrz8o7BXmH0JwsIMAjHLzfxdP4ukGVpibVfnM18cfani8U4YwvPgDaV_FotExeb-opg7HC_3ITqy-e821KfMuTEuUAOf/s800/rayas.png") repeat scroll 0 0 transparent; border-collapse:collapse;
font-family:"Lucida Sans Unicode","Lucida Grande",Sans-Serif;
font-size:12px;
margin:20px;
text-align:left;
width:480px;
}
#fondo-celda-b thead tr {
background:url("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7SZd69O73qGrPLvajWX0I9olFDoAzplFbaAmrvxlYtQOGdE8NFL0NngjWnQDwFbVLm01-NIHf5v7tDm7ZRMxYxIY_Q0l67mgrXko6PqcTJLiEB48MkZZBPuSKscVr_Pxys9cV3v6C1fIx/s800/rayas-encabezado.png") repeat scroll 0 0 transparent;
}
#fondo-celda-b th {
border-bottom:1px solid #FFFFFF;
color:#003399; font-size:13px;
font-weight:normal;
padding:8px;
}
#fondo-celda-b td {
border-bottom:1px solid #FFFFFF;
border-top:1px solid transparent;
color:#666699; padding:8px;
}
#fondo-celda-b tr:hover td {
background:none repeat scroll 0 0 #FFFFFF; color:#333399;
}

12. Fondo Celda /Púrpura

DECOCTHEXBINSymbolHTML NumberHTML NameDescription
4806030001100000&#48;-cero
4906131001100011&#49;-uno
5006232001100102&#50;-dos
5106333001100113&#51;-tres
5206434001101004&#52;-cuatro
5306535001101015&#53;-cinco
5406636001101106&#54;-seis
5506737001101117&#55;-siete
5607038001110008&#56;-ocho
5707139001110019&#57;-nueve


#fondo-celda-c {
background:url("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjch6njeVElf62w7QSOHa7DEnW60GPdNjqqQp6y8U-NFOZT34vbL-6umLGgCjKAoqDcv8kFbUXl1IKKXo3fF6iiwXMMuVm3NJcz8IqAPqbNQtSm37IYs7PoAemMIRT5hkOwnSz_-2ykcjWl/s800/patron.png") repeat scroll 0 0 transparent;
border-collapse:collapse;
font-family:"Lucida Sans Unicode","Lucida Grande",Sans-Serif;
font-size:12px;
margin:20px;
text-align:left;
width:480px;
}
#fondo-celda-c thead tr {
background:url("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNqIHLvfZsm73UY3Sk45d1hhZzT1vVwY0rHpO653wYU1w1B4o5B9Eu7jeM2fwB_1ZXMHUu_gHhjEhEmQcCMQiImzbv9giQSUne7NcDaPpq8vAmjlMYILbUtr80BajwWmXlABfzECP6e1rr/s800/patron-encabezado.png") repeat scroll 0 0 transparent;}
#fondo-celda-c th {
border-bottom:1px solid #FFFFFF;
color:#003399;
font-size:13px;
font-weight:normal;
padding:8px;
}
#fondo-celda-c td
{
border-bottom:1px solid #FFFFFF;
border-top:1px solid transparent;
color:#666699; padding:8px;
}
#fondo-celda-c tbody tr:hover td {
background:none repeat scroll 0 0 #CDCDEE; color:#333399;
}

martes, 26 de octubre de 2010

Determinar redireccionamiento de URL's (redirects)


Un ejemplo básico para determinar si un determinado URL redirecciona a otra dirección.
La función de esta clase obtiene el código de respuesta de la conexión HTTP y si es cualquiera de los códigos de redireccionamiento: 301/302/303, obtiene la dirección redireccionada del encabezado "Location".

import java.net.HttpURLConnection;
import java.net.URL;

public class URLUtils {

/**
* Prints the redirect URL for the provided input URL (if it applies).
* @param url
*/
public static void printRedirect(String url) {
try {
URL urlToPing = new URL(url);
HttpURLConnection urlConn = (HttpURLConnection) urlToPing
.openConnection();
// Needed to check if it is a redirect.
urlConn.setInstanceFollowRedirects(false);

// It's any of these response codes: 301/302/303
if(urlConn.getResponseCode() == HttpURLConnection.HTTP_MOVED_PERM
|| urlConn.getResponseCode() == HttpURLConnection.HTTP_MOVED_TEMP
|| urlConn.getResponseCode() == HttpURLConnection.HTTP_SEE_OTHER) {
System.out.println("URL <" + url + "> redirects to: <"
+ urlConn.getHeaderField("Location") + ">, Response Code: " +
+ urlConn.getResponseCode());
}else {
System.out.println("URL <" + url + "> has no redirect, Response Code: "
+ urlConn.getResponseCode());
}
} catch(Exception e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
URLUtils.printRedirect("http://www.google.com");
URLUtils.printRedirect("http://www.crjug.org");
}
}


Salida:


URL <http://www.google.com> redirects to: <http://www.google.co.cr/>, Response Code: 302
URL <http://www.crjug.org> has no redirect, Response Code: 200

miércoles, 13 de octubre de 2010

Obtener Valor Serializado de un Objeto en un String

Hasta hace poco pensaba que existía una manera directa de obtener el valor serializado de un objeto como una cadena de caracteres (String). Necesitaba el valor String para guardarlo en una base de datos. Resulta que todos los ejemplos que encontraba grababan el valor serializado en bytes en un archivo.

Finalmente encontré una solución en un foro que quiero replicar en este blog. La solución require utilizar esta clase que tiene licencia GPL:Base64
package com.eol.common.util;
package com.eol.common.util;

//Copyright 2003-2010 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
//www.source-code.biz, www.inventec.ch/chdh
//
//This module is multi-licensed and may be used under the terms
//of any of the following licenses:
//
//EPL, Eclipse Public License, V1.0 or later, http://www.eclipse.org/legal
//LGPL, GNU Lesser General Public License, V2.1 or later, http://www.gnu.org/licenses/lgpl.html
//GPL, GNU General Public License, V2 or later, http://www.gnu.org/licenses/gpl.html
//AL, Apache License, V2.0 or later, http://www.apache.org/licenses
//BSD, BSD License, http://www.opensource.org/licenses/bsd-license.php
//
//Please contact the author if you need another license.
//This module is provided "as is", without warranties of any kind.


/**
* A Base64 encoder/decoder.
*
* <p>
* This class is used to encode and decode data in Base64 format as described in RFC 1521.
*
* <p>
* Project home page: <a href="http://www.source-code.biz/base64coder/java/">www.source-code.biz/base64coder/java</a><br>
* Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland<br>
* Multi-licensed: EPL / LGPL / GPL / AL / BSD.
*/
public class Base64Coder {

//The line separator string of the operating system.
private static final String systemLineSeparator = System.getProperty("line.separator");

//Mapping table from 6-bit nibbles to Base64 characters.
private static char[] map1 = new char[64];
static {
int i=0;
for (char c='A'; c<='Z'; c++) map1[i++] = c;
for (char c='a'; c<='z'; c++) map1[i++] = c;
for (char c='0'; c<='9'; c++) map1[i++] = c;
map1[i++] = '+'; map1[i++] = '/'; }

//Mapping table from Base64 characters to 6-bit nibbles.
private static byte[] map2 = new byte[128];
static {
for (int i=0; i<map2.length; i++) map2[i] = -1;
for (int i=0; i<64; i++) map2[map1[i]] = (byte)i; }

/**
* Encodes a string into Base64 format.
* No blanks or line breaks are inserted.
* @param s A String to be encoded.
* @return A String containing the Base64 encoded data.
*/
public static String encodeString (String s) {
return new String(encode(s.getBytes())); }

/**
* Encodes a byte array into Base 64 format and breaks the output into lines of 76 characters.
* This method is compatible with <code>sun.misc.BASE64Encoder.encodeBuffer(byte[])</code>.
* @param in An array containing the data bytes to be encoded.
* @return A String containing the Base64 encoded data, broken into lines.
*/
public static String encodeLines (byte[] in) {
return encodeLines(in, 0, in.length, 76, systemLineSeparator); }

/**
* Encodes a byte array into Base 64 format and breaks the output into lines.
* @param in An array containing the data bytes to be encoded.
* @param iOff Offset of the first byte in <code>in</code> to be processed.
* @param iLen Number of bytes to be processed in <code>in</code>, starting at <code>iOff</code>.
* @param lineLen Line length for the output data. Should be a multiple of 4.
* @param lineSeparator The line separator to be used to separate the output lines.
* @return A String containing the Base64 encoded data, broken into lines.
*/
public static String encodeLines (byte[] in, int iOff, int iLen, int lineLen, String lineSeparator) {
int blockLen = (lineLen*3) / 4;
if (blockLen <= 0) throw new IllegalArgumentException();
int lines = (iLen+blockLen-1) / blockLen;
int bufLen = ((iLen+2)/3)*4 + lines*lineSeparator.length();
StringBuilder buf = new StringBuilder(bufLen);
int ip = 0;
while (ip < iLen) {
int l = Math.min(iLen-ip, blockLen);
buf.append (encode(in, iOff+ip, l));
buf.append (lineSeparator);
ip += l; }
return buf.toString(); }

/**
* Encodes a byte array into Base64 format.
* No blanks or line breaks are inserted in the output.
* @param in An array containing the data bytes to be encoded.
* @return A character array containing the Base64 encoded data.
*/
public static char[] encode (byte[] in) {
return encode(in, 0, in.length); }

/**
* Encodes a byte array into Base64 format.
* No blanks or line breaks are inserted in the output.
* @param in An array containing the data bytes to be encoded.
* @param iLen Number of bytes to process in <code>in</code>.
* @return A character array containing the Base64 encoded data.
*/
public static char[] encode (byte[] in, int iLen) {
return encode(in, 0, iLen); }

/**
* Encodes a byte array into Base64 format.
* No blanks or line breaks are inserted in the output.
* @param in An array containing the data bytes to be encoded.
* @param iOff Offset of the first byte in <code>in</code> to be processed.
* @param iLen Number of bytes to process in <code>in</code>, starting at <code>iOff</code>.
* @return A character array containing the Base64 encoded data.
*/
public static char[] encode (byte[] in, int iOff, int iLen) {
int oDataLen = (iLen*4+2)/3; // output length without padding
int oLen = ((iLen+2)/3)*4; // output length including padding
char[] out = new char[oLen];
int ip = iOff;
int iEnd = iOff + iLen;
int op = 0;
while (ip < iEnd) {
int i0 = in[ip++] & 0xff;
int i1 = ip < iEnd ? in[ip++] & 0xff : 0;
int i2 = ip < iEnd ? in[ip++] & 0xff : 0;
int o0 = i0 >>> 2;
int o1 = ((i0 & 3) << 4) | (i1 >>> 4);
int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
int o3 = i2 & 0x3F;
out[op++] = map1[o0];
out[op++] = map1[o1];
out[op] = op < oDataLen ? map1[o2] : '='; op++;
out[op] = op < oDataLen ? map1[o3] : '='; op++; }
return out; }

/**
* Decodes a string from Base64 format.
* No blanks or line breaks are allowed within the Base64 encoded input data.
* @param s A Base64 String to be decoded.
* @return A String containing the decoded data.
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
*/
public static String decodeString (String s) {
return new String(decode(s)); }

/**
* Decodes a byte array from Base64 format and ignores line separators, tabs and blanks.
* CR, LF, Tab and Space characters are ignored in the input data.
* This method is compatible with <code>sun.misc.BASE64Decoder.decodeBuffer(String)</code>.
* @param s A Base64 String to be decoded.
* @return An array containing the decoded data bytes.
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
*/
public static byte[] decodeLines (String s) {
char[] buf = new char[s.length()];
int p = 0;
for (int ip = 0; ip < s.length(); ip++) {
char c = s.charAt(ip);
if (c != ' ' && c != '\r' && c != '\n' && c != '\t')
buf[p++] = c; }
return decode(buf, 0, p); }

/**
* Decodes a byte array from Base64 format.
* No blanks or line breaks are allowed within the Base64 encoded input data.
* @param s A Base64 String to be decoded.
* @return An array containing the decoded data bytes.
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
*/
public static byte[] decode (String s) {
return decode(s.toCharArray()); }

/**
* Decodes a byte array from Base64 format.
* No blanks or line breaks are allowed within the Base64 encoded input data.
* @param in A character array containing the Base64 encoded data.
* @return An array containing the decoded data bytes.
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
*/
public static byte[] decode (char[] in) {
return decode(in, 0, in.length); }

/**
* Decodes a byte array from Base64 format.
* No blanks or line breaks are allowed within the Base64 encoded input data.
* @param in A character array containing the Base64 encoded data.
* @param iOff Offset of the first character in <code>in</code> to be processed.
* @param iLen Number of characters to process in <code>in</code>, starting at <code>iOff</code>.
* @return An array containing the decoded data bytes.
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
*/
public static byte[] decode (char[] in, int iOff, int iLen) {
if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4.");
while (iLen > 0 && in[iOff+iLen-1] == '=') iLen--;
int oLen = (iLen*3) / 4;
byte[] out = new byte[oLen];
int ip = iOff;
int iEnd = iOff + iLen;
int op = 0;
while (ip < iEnd) {
int i0 = in[ip++];
int i1 = in[ip++];
int i2 = ip < iEnd ? in[ip++] : 'A';
int i3 = ip < iEnd ? in[ip++] : 'A';
if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
int b0 = map2[i0];
int b1 = map2[i1];
int b2 = map2[i2];
int b3 = map2[i3];
if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
int o0 = ( b0 <<2) | (b1>>>4);
int o1 = ((b1 & 0xf)<<4) | (b2>>>2);
int o2 = ((b2 & 3)<<6) | b3;
out[op++] = (byte)o0;
if (op<oLen) out[op++] = (byte)o1;
if (op<oLen) out[op++] = (byte)o2; }
return out; }

//Dummy constructor.
private Base64Coder() {}

}


La siguiente clase sería la clase utilitaria para generar el valor String del objeto y para volver a construir el objeto a partir del valor serializado.

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
* Utility class to serialize and unserialize objects.
* @author gsolano
*
*/
public class ObjectSerializationUtils {

/**
* Gets a String value of the object's serialize value.
* @param object
* @return
* @throws IOException
*/
public static String serializeObject(Serializable object) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream( baos );
oos.writeObject( object );
oos.close();
return new String( Base64Coder.encode( baos.toByteArray() ) );
}

/**
* Rebuilds an object (unserializes) from the serialize String value.
* @param serializeString
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public static Object unserializeObject( String serializeString )
throws IOException , ClassNotFoundException {
byte [] data = Base64Coder.decode( serializeString );
ObjectInputStream ois = new ObjectInputStream(
new ByteArrayInputStream( data ) );
Object o = ois.readObject();
ois.close();
return o;
}
}

Una sencilla prueba:

import java.io.Serializable;

public class MySerializeClass implements Serializable{

private static final long serialVersionUID = 5021383691938311325L;

protected String fooValue;

public String getFooValue() {
return fooValue;
}

public void setFooValue(String fooValue) {
this.fooValue = fooValue;
}
}


import java.io.IOException;

public class StringSerializationTest {

public static void main(String[] args) {
MySerializeClass mySerializeClass = new MySerializeClass();
mySerializeClass.setFooValue("I will be turned in a string soon...");

try {
String DNACode = ObjectSerializationUtils.serializeObject(mySerializeClass);
System.out.println(DNACode);
MySerializeClass clon = (MySerializeClass)
ObjectSerializationUtils.unserializeObject(DNACode);
System.out.println(clon.getFooValue());

} catch (IOException e) {
e.printStackTrace();
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}


Salida:


rO0ABXNyABBNeVNlcmlhbGl6ZUNsYXNzRa+J3QbsLJ0CAAFMAAhmb29WYWx1ZXQAEkxqYXZhL2xhbmcvU3RyaW5nO3hwdAAkSSB3aWxsIGJlIHR1cm5lZCBpbiBhIHN0cmluZyBzb29uLi4u
I will be turned in a string soon...

lunes, 11 de octubre de 2010

Ejemplo Sencillo del patrón Singleton

El patrón Singleton es quizás uno de los patrones más básicos que deberíamos entender por su extendida aplicabilidad en el diseño de clases, y también porque es uno de los patrones más usados en las entrevistas para probar conocimientos en patrones de diseño.

No pretendo extenderme mucho en la descripción del patrón. La definición más simple es que provee una sola instancia de un objeto para una determinada clase. A continuación dejo un ejemplo de cómo implementar este patrón:


/**
* Simple example of the Singleton pattern.
* @author gsolano
*
*/
public class MySingleton {

   /**
   * The only instance that is going to be used.
   */
   protected static MySingleton myInstanceOfSingleton;
   protected String fooValue;

   /**
   * Gets the instance of this class.
   * @return
   */
   public static MySingleton getInstance() {
      if(myInstanceOfSingleton == null) {
         synchronized (MySingleton.class) {
            myInstanceOfSingleton = new MySingleton();
         }
      }
      return myInstanceOfSingleton;
   }

   /**
   * Private constructor to prohibit instantiation of this class.
   */
   private MySingleton() {
      fooValue ="Hello World!!";
   }

   public void setFooVaule(String value) {
      this.fooValue = value;
   }

   public void printFooValue() {
      System.out.println(fooValue);
   }

   public static void main(String[] args) {
      MySingleton mySingleton = MySingleton.getInstance();
      mySingleton.printFooValue();
      mySingleton.setFooVaule("This is Singleton!!");

      MySingleton mySecondSingleton = MySingleton.getInstance();
      // Prints the  value set in mySingleton variable.
      mySecondSingleton.printFooValue();
   }
}
Salida:



Cabe resaltar dos aspectos importantes de este ejemplo de Singleton.
  1. El constructor es declarado como privado para prohibir crear muútiples instancias de esta clase.
  2. La única puerta para acceder la clase es a través del método “getInstance” que internamente instancia una sola vez el objeto que se utilizará en todo el contexto de la aplicación, y el resto de las veces retorna una referencia a la única instancia de la clase.

martes, 28 de septiembre de 2010

java.lang.UnsupportedOperationException: This parser does not support specification "null" version "null"

Estaba montando JCS en mi proyecto web donde tengo Strut 1.3 agregando la dependencia de Maven:
<dependency>
<groupId>jcs</groupId>
<artifactId>jcs</artifactId>
<version>1.3</version>
</dependency>

Y de repente comencé a tener la siguiente excepción al inicializar la aplicación en Tomcat:

2010-09-28 10:44:36 ERROR [ContainerBackgroundProcessor[StandardEngine[Catalina]]] (Digester.java:789) - Digester.getParser:
java.lang.UnsupportedOperationException: This parser does not support specification "null" version "null"
at javax.xml.parsers.SAXParserFactory.setXIncludeAware(SAXParserFactory.java:448)
at org.apache.commons.digester.Digester.getFactory(Digester.java:534)
at org.apache.commons.digester.Digester.getParser(Digester.java:786)
at org.apache.commons.digester.Digester.getXMLReader(Digester.java:1058)
at org.apache.commons.digester.Digester.parse(Digester.java:1887)
at org.apache.struts.action.ActionServlet.initServlet(ActionServlet.java:1144)
at org.apache.struts.action.ActionServlet.init(ActionServlet.java:328)
at javax.servlet.GenericServlet.init(GenericServlet.java:212)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1173)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:993)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4187)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4496)
at org.apache.catalina.core.StandardContext.reload(StandardContext.java:3228)
at org.apache.catalina.loader.WebappLoader.backgroundProcess(WebappLoader.java:403)
at org.apache.catalina.core.ContainerBase.backgroundProcess(ContainerBase.java:1309)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1601)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1610)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1610)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1590)
at java.lang.Thread.run(Thread.java:619)

Después de investigar un poco en Google descubrí una dependencia de JCS estaba causando este conflicto: Xerces

Así que aprendí algo nuevo del pom.xml, cómo agregar una exclusión:
<dependency>
<groupId>jcs</groupId>
<artifactId>jcs</artifactId>
<version>1.3</version>
<exclusions>
<exclusion>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
</exclusion>
</exclusions>
</dependency>

viernes, 24 de septiembre de 2010

Aplicación portable gratuita para creación de diagramas

Hace poco me topé con esta aplicación portable llamada Dia para crear todo tipo de diagramas como Entidad-Relación, UML, de flujo, etc. Digamos que es como una versión más "light" de Office Visio pero muy funcional para crear diagramas rápidos.

Un par de ejemplos:




Exportado:



Diagrama UML (clases)


Exportado

viernes, 17 de septiembre de 2010

10 tips para conducir tu carrera como desarrollador de software

Recientemente leía un artículo interesante acerca de tips profesionales para desarrolladores y diseñadores web , así que comencé a reflexionar acerca de mi propio conjunto de tips que recomendaría para conducir la carrera como un típico desarrollador de software. Muchos puntos podrían agregarse a esta lista, pero solamente es mi percepción personal en esta etapa de mi desarrollo profesional, contextualizado en un país llamado “tercer mundista”.

  1. Disfruta lo que haces. Este punto estará probablemente presente en toda lista de tips profesionales para cualquier carrera. Es realmente tedioso tratar de ser mejor en algo en lo cual odias con todo el corazón. Si no te gusta programar, y nada relacionado con ello, entonces no lo hagas. Existen otros campos de las ciencias de la computación e informática en los cuales se puede uno enfocar, o inclusive no tan relacionados pero donde se pueden aplicar los talentos “computínes”.
  2. Toma riesgos. El tip anterior podría llevarnos a este. Hace tres años atrás, tuve que tomar la decisión de si renunciar a una gran compañía multinacional, donde no estaba muy a gusto hacia donde estaba tomando rumbo mi carrera profesional. Estaba haciendo prácticamente 0 desarrollo en mi trabajo en un área muy especializada del negocio de la compañía. Es difícil renunciar al gran ambiente laboral que te puede ofrecer una compañía enlistada en las 100 más exitosas del mundo, pero tenía que determinar si valía la pena continuar evaluando cuales eran mis expectativas profesionales. Al final me moví a una empresa más pequeña de desarrollo donde he aprendido mucho más del campo en el cual quiero crecer.
  3. Cela tu tiempo. El tiempo es una de las cosas más importantes de nuestras vidas. Por esa razón no deberíamos dejar que otras personas, o circunstancias, mal gasten nuestro tiempo en cosas sin importancia. Muchas de las compañías en Costa Rica funcionan con proyectos a corto y mediano plazo. Eso significa que cuando el proyecto termina (algunas veces abruptamente) las personas del proyecto, si no tienen otro proyecto a donde ser relocalizadas, son puestas en la poco popular “banca”. Un grupo de personas se sienten cómodas por tener un lugar en la banca (especialmente las instituciones gubernamentales donde pareciera haber un estado perpetuo de banca :/) porque se siente bien malgastar nuestro tiempo en actividades poco productivas como Youtube o Facebook. Pero así como la comida basura, nada realmente bueno resulta de solamente sentirse rico. ¿Cuál es mi consejo? Ser más proactivo durante el tiempo de banca, actualízate en una nueva tecnología, trabaja en un proyecto personal, y sobre todo, hazle saber a tu superior que no estás contento con tu estado actual. Esto le hace saber al resto de personas que te consideras una pieza valiosa de la compañía y que no te gusta estar sin hacer nada.
  4. Escribe un blog. Sí, sabemos que tu novi@ va a reforzar tu estatus de geek, pero verdaderamente he aprendido que vale la pena tener como pasatiempo esta saludable actividad. Primeramente uno aprende a escribir bien. Seamos honestos, como ingenieros de software, en promedio tendemos a apestar en redacción. Un blog es una buena manera de afinar nuestras habilidades en redacción. Inclusive nos sentimos aún más motivados cuando sabemos que nuestros artículos están creando atención en la blogosfera. Así que con esta motivación intentamos ser más claros con los conceptos que queremos explicar, inclusive buscando nuestro propio estilo de escritura. Intentamos ser más creativos, graciosos e interesantes. El blog es también un repositorio histórico de todas nuestras luchas técnicas. ¿Cuántas veces no has sentido ese “déjà vu” al enfrentarte a un problema pero no puedes recordar cómo fue que lo resolviste? Precisamente el blog puede ser útil en esas ocasiones, que además se puede utilizar para dejar de referencia a otras personas, especialmente si tienes una posición de liderazgo en tu equipo. Te ahorras el tiempo de tener que explicar la misma respuesta a una pregunta sobre un tema recurrente. Podrías hacer todo un artículo acerca de las ventajas de mantener un blog, pero solo quiero terminar mencionando que los conceptos quedan más claros en nuestra mente y son más fáciles de recordar cuando los escribimos.
  5. Manténte en lo ultimo en tecnologías y noticias. Hoy en día, resulta ser más sencillo estar al tanto de lo más reciente del campo. Simplemente podemos suscribirnos a toda una gama amplia de feeds, escogiendo aquellos que más nos interesen y consideramos útiles. Los podcast son otra excelente opción que podemos usar para escuchar acerca de las noticias del momento. Podemos oírlos de regreso a casa o en una sesión de ejercicios. Son simples y prácticos.
  6. Seamos “pura vida” con la gente. Ciertamente, la mayoría de nosotros no trabajamos como mercenarios. Normalmente trabajamos en equipos donde existen otras entidades inteligentes llamadas “compañeros de trabajo”. Si no podemos funcionar en equipo, nos veremos limitados a los proyectos que solo podemos finalizar por cuenta propia. Casi todos los proyectos significativos requieren tener un grupo de personas trabajando juntas para llegar a un producto final. Así que seamos buena gente.
  7. Ve fuera de tus fronteras. No te limites solamente a lo que puedes hacer en tu trabajo. Forma parte de una comunidad o grupo de usuarios. Y no tengas miedo, no significa que vayas a gastar todo tu tiempo en computadoras y por tanto ser percibido como un nerd antisocial. Una reunión regular cada cierto tiempo no duele mucho. Otros profesionales de otros saberes tienen toda la disposición de ir a reuniones con colegas, inclusive pagando sumas de dinero significativas. Así que tomemos ventaja de las oportunidades que se nos presentan, sobre todo si son gratuitas, y hagámonos notar. Nunca se sabe cuando una buena oportunidad puede venir fuera de nuestro hábitat.
  8. Da la milla extra. Sé que este puede ser un slogan algo gastado, especialmente cuando algunas compañías lo utilizan para “motivar” a sus empleados a trabajar horas extras sin pago extra. Pero podemos utilizarlo correctamente para decir que debemos ir más allá de las expectativas. Una aplicación práctica es tener el principio de intentar siempre terminar tu trabajo lo más pronto posible sin afectar la calidad del mismo. Conozco por experiencia de observar el comportamiento de otros colegas, que es común para muchas personas jugar con las fechas de entrega. Un programador con colmillo podría rápidamente escanear la descripción de la tarea, determinar que puede terminarla en la mitad de lo estimado y básicamente gastar la otra mitad en diversas actividades; digamos que menos productivas. Yo considero esto una actitud poco ética. Uno puede estar bien con las fechas de entrega y aun así ser un empleado mediocre.
  9. No juegues al guru. Deja que otras personas te traten como un guru si es el caso, pero no lo promuevas tu mismo. ¿Qué significa esto? Significa que deberías permanecer con mente abierta para las sugerencias de otras personas, y no pretender que sabes la mejor manera de resolver todo. Esto se vuelve cada vez más difícil a medida que crece nuestro “seniority”. Hay mucho orgullo envuelto, especialmente cuando lidiamos con el código de otras personas. No podemos pretender que todo mundo codifique al mismo estilo que nos gusta. Lo que si podemos hacer es sugerir buenas prácticas, pero siempre recordando usar la amabilidad para no jugar de “sabelo-todo”. Recordemos mantenernos moldeables.
  10. Aprende inglés. Desafortunadamente para algunos, las academias de inglés no están bromeando cuando muestran puertas que se cierran a las personas que no dominan tan importante idioma. Muchas de las compañías de desarrollo en Costa Rica interactúan con clientes del Norte. La frase anti yanqui “Gringos Go Home” no aplica mucho en nuestro contexto. Se recomienda comenzar el estudio serio y constante del inglés en época universitaria para estar listo una vez que se comience aplicar en el mercado laboral.