Armando un servlet desde bien abajo.

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.

Etiquetas: , , , , ,

10 comentarios to “Armando un servlet desde bien abajo.”

  1. andy Says:

    voy a intertarlo como dices, espero que resulte.gracias por darte tiempo.

  2. andy Says:

    Miguel ,gracias me saliooooooo, nada pues sigue apoyandonos con temas de JAVA EMPRESARIAL .chauu

  3. Pablo Dante Says:

    Buen día.
    Estoy tratando de ejecutar el archivo .java pero me trae errores en las siguientes líneas:

    C:\>javac ContarPalabrasServlet.java

    ContarPalabrasServlet.java:25: cannot find symbol
    symbol : class PrintWriter
    location: class com.palabras.controller.ContarPalabrasServlet
    PrintWriter out = response.getWriter();
    ^
    ContarPalabrasServlet.java:35: cannot find symbol
    symbol : method isEmpty()
    location: class java.lang.String
    if (texto == null || texto.isEmpty()) {
    ^
    2 errors

    C:\>

    Puse el archivo .java porque me es más facil para compilar este caso pero no puedo resolver estos inconvenientes. No soy muy ducho en JAVA, pero necesitaría una mano para poder resolver el ejercicio.
    Gracias
    Un saludos Cordial.

    • Miguel Says:

      El problema del PrintWriter es que le falta el import en la parte superior. Si usás un IDE como eclipse y presiona Ctrl – Shift – O te incluye los imports automáticamente.

      Deberías incluir import java.io.PrintWriter; en la parte superior. Si lees en el texto hay un ejemplo de como compilar incluyendo los libs correspondientes a Servlets.

      Mucha suerte!

      • Pablo Dante Says:

        Hola Miguel.
        Ya lo resolví de la siguiente forma:
        import java.io.*;
        import javax.servlet.*;
        import javax.servlet.http.*;

        Muchas gracias por la ayuda.

        Ahora tengo otro inconveniente con esta parte:
        jar cf palabras.war -C webapps

        Dicho comando lo ejecuto desde la consola pero no sé porque no me lo genera ???
        Dentro del Tomcat tengo las dos carpetas como menciona el ejemplo: src y webapps….
        Ahora bien… Tengo que correr la sentencia desde dentro de la carpeta donde tengo el Tomcat? O desde la raíz?
        Si me puede guiar en esto lo voy a agradecer.
        Saludos y gracias nuevamente.

  4. Pablo Dante Says:

    Hola buen día.
    Necesito una mano con este ejercicio …
    Cuando voy a disparar el evento para contar me dice lo siguiente:
    “El recurso requerido (/palabras/ContarPalabras) no está disponible.”
    Qué estoy haciendo mal?
    Saludos

  5. Pablo Dante Says:

    Otra consulta…
    para compilar lo siguiente:
    javac -cp C:\Tomcat 5.5\lib\servlet-api.jar \
    -d webapp\WEB-INF\classes \
    src\com\palabras\controller\ContarPalabrasServlet.java
    1) Esto va en una sol línea?
    2) las barras \ son sparadores?
    3) Y de ser así … por qué me dice File not found. Si las ruta especificada existe el .java???
    Saludos
    PD: estoy creando el .war desde la consola del SO. muchas gracias

  6. Miguel Says:

    Estimado,

    1y2) Eso podría ir en una sola línea. Las contrabarras (\) se usan para separar una línea en varias, de esa manera es más legible.

    3) Verifica los paths que están ahí. El archivo .java debería estar en en el directorio src\com\palabras\controller, el directorio webapp\WEB-INF\classes existe y tenés el tomcat instalado en el directorio al que se hace referencia.

    Mucha suerte!

    • Pablo Dante Says:

      Buen día.
      Ya logre varios avances en esta parte.
      Pude compilar el archivo.java y tengo la clase del mismo.
      la (\) separadora en Windows no parece funcionar… es posible que usted utilice Linux?… Para ello utilicé “;” pero tampoco pude.
      Es más el conflicto parece que es el siguiente “Tomcat 5.5” al parecer no le gusta.
      El archivo.java está en la ruta indicada como se explica en este artículo. Al final la Clase la tuve que generar o compilar desde la raíz misma del “ContarPalabrasServlet.java” y después pegarla en la carpeta “webapp\WEB-INF\classes”.
      Como verá no soy muy bueno en JAVA puesto que todo esto que le pregunto radica en una serie de inconvenientes que se me presentaron cuando quise integrar el CKFinder dentro del CKEditor. Pero en ambiente Java y para ello de servlet no entendía mucho. Por eso estoy tratando de ver como se compone. Por ese motivo lo molesto tanto porque no se nada de Java.
      Si es posible me pueda dar una mano ya que más o menos le voy agarrando la mano a este tema me gustaría Explicarle, desde cero, qué estoy haciendo y como sé que la tienen “clara” con Java. Bueno pensé en tenerlo como referente.
      Muchas gracias.
      PD: no le molesta qué le siga consultando? Saludos

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s


A %d blogueros les gusta esto: