Archive for 19 mayo 2012

como llegar a ser el programador ideal

mayo 19, 2012

En la película Hero con Jet Li, en una escena se rebela del ideal al que puede llegar un espadachín. Cabe entender que en esa época se podía vivir de manejar una espada con cierta pericia. El avance de la tecnología llevó a que uno pueda vivir de escribir programas. Salvando grandes distancias, creo que se puede obtener cierto paralelismo entre lo que la peli dice de como se llega a ser un esapadachín ideal y de como se llega a ser un programador ideal.

Va acá una adaptación en inglés, porque me suena más lindo. Pero vale tanto en español como en inglés. El original es en chino mandarín.

Codemanship’s first achievement
is the unity of man and code
Once this unity is attained
even a shell script can solve any requirement
The second achievement is when
the code exists in one’s heart
when absent from one’s hand
One can deploy a full distributed system
even with bare hands
Codesmanship’s ultimate achievement
is the absence of the code
in both hand and heart
The codeman is at peace
with the rest of the world
He vows not to cludge
and to bring peace to mankind

Creo que el primer logro es cuando aprendés decentemente un lenguaje y conocés medianamente los recursos que te da el sistema operativo para hacer casi cualquier programa.

Obtuviste el segundo cuando lográs aprehender el concepto de declaratividad.

Finalmente, podés decir que llegaste al último logro cuando empezás a irte del laburo a la hora que acordaste el día que entraste.

Anuncios

Problemas con charset con JSF y messages.properties

mayo 14, 2012

Estoy desarrollando un aplicación web con JBOSS AS 7.1 usando JSF. Este tipo de emprendimientos los hacía usando Spring. Entiendo que Java EE 6 tiene muchas cosas que antes eran características de Spring, por eso me animé al salto.

Todo venía bien (con las marchas y contramarchas propias del self made java ee developer) hasta que tope con un problema de encoding del message.properties.

Parece que por defecto te imprime los carácteres en el locale de la máquina. Mi sistema operativo para mi idioma usa iso-latin-1. Los templates están en utf8, el message.properties está en utf-8. Desastre en puerta.

Googleando un poco me enteré que por cuestiones de implementación, las properties no puede usar código que no estén en el mapa de latin-1.

Una manera de evitar el problema era representar los caracteres especiales con la notación \u####, donde #### es el código UTF del carácter. Esto se podía hacer trabajando con el archivo en utf8 y a la hora de desplegar, convertir el archivo original en código ASCII + \u#### usando la herramienta native2ascii provista con la jdk.

Medio lastimoso.

En la url citada más arriba proponen sustituir la implementación del resource bundle por una que soporte UTF-8. El código lo puede ver en el artículo Internationalization in JSF with UTF-8 encoded properties files. El único cambio que le hice fue usar otra ubicación para el archivo por messages.properties, cambiándola en la constante BUNDLE_NAME = “messages”.

Con eso funcionó de pelos.

Por un momento extrañé a Spring 😛

Armando un servlet desde bien abajo.

mayo 12, 2012

Este artículo de hoy no busca ser una receta de cómo hacer algo en el ambiente laboral. Muchas de estas que vamos a ver las resuelven herramientas como Eclipse, maven, etc. El objetivo es un “estudio” un poco más hondo de lo que pasa desde que escribimos nuestro servlet hasta que llega al servidor y queda corriendo.

El servlet lo vengo prometiendo desde un post anterior. La idea de bajar a la línea de comando surgió de la idea de probar esto de los servlets sin tener el Eclipse ni el Netbeans. Así que estábamos linux, vim, la jvm y yo. Comparto la experiencia porque creo que puede ayudar a alguien que trabaja con java web a entender mejor y saber realmente lo que hacen por nosotros las herramientas.

El ejemplo lo vamos a hacer para Windows esta vez.

¿qué vamos a hacer?

Un contador de palabras. El contador de palabra tiene dos componentes: Un formulario en el que escribimos un texto y un servlet que cuenta las palabras y nos muestra por pantalla el resultado.

Nos va a quedar un a archivo palabras.war, que es la manera en que la especificacion de java empaqueta las aplicaciones web. Ese .war va a tener un formulario html y un servlet.

¿qué necesitamos?

Ganas y:

  • un jdk de 1.5 en adelante.
  • un contenedor de servlets, en este caso tomcat 6 , bajarlo como .zip, no vale usar instalador.
  • un editor de texto.
  • un poco de maña con variables de entorno y scripts .bat.

Conociendo el contenedor de Servlets

Tomcat es el contenedor de servlets de uso más extendido, así que no es mal plan conocer algunas cosas básicas. Es una aplicación java, por lo cuál corre desde una JRE. Desde la versión 5, no es necesaria la JDK. Sin embargo como nosotros vamos a compilar y armar nuestro proyecto a mano, vamos a necesitarla de todos modos.

El paquete zip viene con los siguientes directorios.

apache-tomcat-6.x.x/
  bin/
  conf/
  lib/
  logs/
  temp/
  webapps/
  work/

A vuelo de pájaro: el directorio bin contiene los ejecutables y los scripts de arranque y de apagado.

El directorio conf tiene los archivos server.xml, web.xml y tomcat-users.xml como más representativos. Permiten configurar los puertos que va a usar, opciones de la web que vamos a mostrar, como cual es la página de inicio y los usuarios de algunas aplicaciones.

Lib tiene los jar que usa para trabajar y que van a usar en común las aplicaciones que carguemos. Nos puede dar una mano para compilar.

temp maneja los archivos temporales

En webapps van las aplicaciones. El objetivo es generar el palabras.war y ponerlo en este directorio. Tomcat sólo lo va a descomprimir y poner en marcha.

work es un directorio que usa internamente tomcat donde despliega las cosas.

Lo descomprimimos y vamos al directorio bin y ahí ejecutamos startup.sh. Va a aparecer una ventanita que nos va  informar lo que está haciendo. Una vez que termine informa: Server statup in x ms.

Windows probablemente nos informe que está queriendo acceder a la red. Le decimos que sí. Lo que está pasando es que al ser un servidor, Windows nos avisa que estamos abriendo algo que está escuchando cosas en la red y que otras computadoras pueden acceder. En realidad no es nada grave porque lo estamos usando nosotros sólos y hoy por hoy lo más probable es que estemos detrás de un router, así que si alguien accede, es alguien de la red local, con lo cuál no hay un peligro cierto.

Abrimos el navegador y accedemos a http://localhost:8080.

Deberíamos ver una pantalla donde nos comenta que estamos viendo, aparece el logo de tomcat y todo feliz.

Manos a la obra.

Primero vamos a armar un directorio, y dentro de este vamos a tener la siguiente estructura:

./
  src/
    com/
      palabras/
        controller/
  webapp/
    WEB-INF/
    classes/

El código de la aplicación va a estar en los siguientes archivos:

  • src/com/palabras/controller/ContarPalabrasServlet.java
    Clase java que implementa el servlet contador de palabras.
  • webapp/index.html
    Archivo HTML que contiene un formulario que usa al servlet contador de palabras.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
  <HEAD>
    <TITLE>Jugando con palabras</TITLE>
  </HEAD>
  <BODY>
    <H1>Jugando con palabras</H1>
    <P>Aplicaci&oacute;n muy sencilla basada en servlets para contar palabras.</P>
    <FORM METHOD="POST" ACTION="ContarPalabras">
      <FIELDSET>
        <P>Escrib&iacute; o peg&aacute; un texto ac&a abajo:</P>
        <TEXTAREA name="texto" ROWS="5"></TEXTAREA>
        <BR/>
        <INPUT TYPE="SUBMIT" value="A contar!" />
      </FIELDSET>
    </FORM>
  </BODY>
</HTML>

Acá empiezan las complicaciones. Hay que compilar y generar el archivo .war.

De estas tareas normalmente se encargan eclipse, ant o maven. Hoy vamos a tratar de hacerlo nosotros.

Los primeros ejemplos que uno compila en java no usan nada más que las clases propias y las clases que ya vienen en rt.jar, o la biblioteca estándar de runtime. Por eso casi no jugamos con el classpath.

package com.palabras.controller;
import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@SuppressWarnings("serial")
public class ContarPalabrasServlet extends HttpServlet {

	public ContarPalabrasServlet() {
		super();
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		manejarRequest(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		manejarRequest(request, response);
	}

	private void manejarRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    	PrintWriter out = response.getWriter();
		out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">");
    	out.println("<HTML>\n");
		out.println("\t<HEAD>\n");
    	out.println("\t\t<TITLE>Contar Palabras</TITLE>\n");
    	out.println("\t</HEAD>\n");
    	out.println("\t<BODY>\n");
		out.println("\t\t<H1>Contar Palabras</H1>\n");

		String texto = (String )request.getParameter("texto");
		if (texto == null || texto.isEmpty()) {
			out.println("\t\t<h2 style=\"color:red;\">Debe ingresar un texto, sino no hay mucho que contar.</h2>\n");
		} else {
			out.println(String.format("\t\t<p>El texto se compone de %d palabras</p>", texto.split("/\\s/").length));
			out.println(String.format("\t\t<textarea readonly=\"true\">%s</textarea>", texto));
		}
    	out.println("\t</BODY>\n");
    	out.println("</HTML>\n\n");
    	out.close();
	}

}

Acá la cosa es un poco distinta. Si queremos compilar nuestro Servlet vamos a fracasar lastimosamente porque no encuentra la clase HttpServlet. Esto es porque la clase no está ni entre nuestro ni entre la biblioteca estándar de java. Bueno, esa biblioteca viene incorporada en el directorio lib/ que mencionábamos más arriba, y servlet-api.jar.

Para compilar, entonces vamos a tener que indicar que el classpath incluya el directorio de tomcat/lib con la opcion –classpath o -cp.

Otra cosa que nos va a pasar es que el .class va a quedar en el mismo directorio de la clase, y nosotros queremos que vaya en una ubicación especial respetando la jerarquía de directorios que corresponde al paquete deonde creamos el servlet (com.palabras.controller). El compilador de java nos da una opción -d RUTA para compilar y dejar los .class con la estructura de subdirectorios. Si compilamos el servlet con la opción -d C:\, notaríamos que aparece en C:\ un directorio com, adentro del último uno llamado palabras y así.

¿Pero, a dónde?

Conociendo los archivos war

El war es un archivo comprimido que adentro contiene una serie de archivos y directorios. La organización interna no es caprichosa y responde a una especificación.

Vamos a trabajar con la especificación 2.4. Las más nuevas tienen una serie de simplificaciones que vamos a obviar para retormarlas cuando estemos un poco más duchos.

El war que vamos a armar tiene la siguiente pinta:

./
  WEB-INF/
    classes/
       com/
         palabras/
           controller/
              ContarPalabrasServlet.class
    web.xml
  index.html

El directorio WEB-INF tiene información que la indican al contenedor características del proyecto, las bibliotecas que la aplicación usa y las clases que componen a la aplicación. Nuestro proyecto es tan sencillo que le alcanza sólo con la carpeta classes.

Ahora que sabemos donde, podemos compilar:

javac -cp c:\apache-tomcat-6.0.35\lib\servlet-api.jar \
      -d webapp\WEB-INF\classes \
      src\com\palabras\controller\ContarPalabrasServlet.java

El parámetro -d webapp\WEB-INF\classes es el que le indica que guarde el resultado de compilar en el directorio WEB-INF\classes.

El archivo web.xml es el descriptor de implementación y es el encargado de indicarle al contenedor de servlets cual es la página de inicio, el nombre con el que tiene que desplegar el proyecto, el mapeo de urls que no correspondan a archivos dentro del war. Si nos fijamos en el formulario dentro de index.html hacemos referencia a una action ContarPalabras, pero no existe ningún archivo que se llame así.

Para que tomcat pueda atender al pedido de ContarPalabras tenemos que indicárselo en el web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="palabras" version="2.4" 
	xmlns="http://java.sun.com/xml/ns/j2ee" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
	<description>
		Jugando con palabras
	</description>
	<display-name>palabras</display-name>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
	</welcome-file-list>
	<servlet>
		<description>
			Servlet para contar palabras
		</description>
		<display-name>ContarPalabras</display-name>
		<servlet-name>ContarPalabrasServlet</servlet-name>
		<servlet-class>com.palabras.controller.ContarPalabrasServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>ContarPalabrasServlet</servlet-name>
		<url-pattern>/ContarPalabras</url-pattern>
	</servlet-mapping>
</web-app>

Bueno, acá hay muchas cosas. El tag welcome-fil-list, contiene una lista de archivos que pueden ser interpretados por el contenedor como archivo por defecto cuando piden la aplicación.

El tag servlet define un servlet, el display-name es el nombre que nos muestra en el servidor, el servlet-name es un identificador del servlet en este contexto y servlet-class es la clase que implementa el servlet. En nuestro caso com.palabras.controller.ContarPalabrasServlet. Hay que poner el nombre completo (FQDN), para que pueda resolverlo buscando en el directorio WEB-INF/classes.

Por último <servlet-mapping> es el que le dice al contenedor que servlet debe resolver que la url del request.

En Java EE 6, la definición y mapeo de servlets se realiza mediante annotations, una de las razones por la cuál el archivo web.xml es optativa.

Ahora que ya tenemos todo armado en el directorio webapp llegó la hora de empaquetarlo y desplegarlo (deployarlo).

Esto se hace usando el comando jar que viene con la jvm. jar nos permite armar paquetes como los .jar, los .war, .ear, etc.

Nos vamos al directorio donde creamos los directorios src y webapp y ejecutamos:

jar cf palabras.war -C webapp .

Nos fijamos en el directorio y tenemos un archivo palabras.war. Podemos abrirlo con WinRar y verificar que tiene lo que esperamos.

Poner en marcha la aplicación será tan difícil como copiar el archivo palabras.war en el directorio webapps dentro del directorio del tomcat.

Tomcat detecta la novedad en el filesystem y realiza el despliegue de la aplicación. Cuando termina, nos lo notifica.

Abrir el navegador e ir a http://localhost:8080/palabras

Si todo fue bien, felicitaciones! Cualquier cosa acá abajo se contestan dudas.