Los servlets nacieron de la necesidad de permitir a los desarrolladores crear contenido de forma dinámica para las webs, sin tener que dejar de utilizar Java, y como alternativa a otras tecnología como CGI.
Los servlets son como los ladrillos de una aplicación web. Son los componentes más básicos con los que se trabaja. Cuando un cliente realiza una petición, el servidor web se encarga de llamar al servlet adecuado según la configuración proporcionada por la aplicación.
WAR
Las librerías y aplicaciones de escritorio Java tradicionales se empaquetan en un fichero con extensión .jar. Las aplicaciones web en cambio se empaquetan en un fichero .war (Web ARchive). En esencia son lo mismo, pero con la extensión cambiada para distinguirlos.
Un fichero war característico suele tener al menos un directorio WEB-INF y un fichero web.xml dentro de él. Un ejemplo más real contendrá más directorios y ficheros, como en la siguiente estructura de ejemplo:
/META-INF /WEB-INF /classes /lib web.xml /images index.html
El directorio classes es donde se ubicarán las clases que formen parte de la aplicación web, y en el directorio lib las librerías (jar) que necesite para su ejecución.
El fichero web.xml es un descriptor de despliegue, ya que desplegar es el nombre con el que se conoce a la acción de instalar una aplicación en un servidor web. Un ejemplo básico de este tipo de ficheros sería como el siguiente:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>hello</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> </web-app>
La etiqueta display-name
asigna un nombre a la aplicación, que será que el que se utilice para diferenciarla del resto de aplicaciones dentro de un servidor. La etiqueta welcome-file-list
contiene el nombre del primer fichero que debe tratar de servirse cuando se acceda a la aplicación. Aunque en realidad es una lista de ficheros, si no existe el primero se intenta con el segundo y así sucesivamente.
Para desplegar una aplicación web normalmente basta con dejar el fichero war dentro de un directorio del servidor web, aunque existen otras posibilidades. Lo normal es que dentro de un proyecto todos los desarrolladores trabajen de una misma manera, marcado por por algún tipo de procedimiento, o simplemente por la costumbre.
Servlets
Un servlet es una clase Java que implementa la interface javax.servlet.Servlet
. Sin embargo, para la mayoría de los desarrollos basta con crear una clase que herede de HttpServlet
, que ya implementa dicha interface:
public class HelloServlet extends HttpServlet { ...
La clase HttpServlet
se encuentra dentro del paquete javax.servlet.http
, no incluido dentro del JDK estándar, por lo que es necesario añadir la dependencia correspondiente en el proyecto con la librería servlet-api.jar.
Si se está utilizando Maven, algo muy recomendable, la dependencia puede añadirse directamente en el fichero pom.xml:
<dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.4</version> <scope>provided</scope> </dependency>
La librería la tiene que proporcionar el servidor web en tiempo de ejecución, de ahí que se utilice el valor provided
en la etiqueta scope
. Como versión se utiliza la 2.4, que a pesar de ser bastante antigua aún se encuentra en muchos entornos de producción. Al final de este artículo se encuentra un apartado con algunas de las novedades incorporadas en la versión 3.0.
La clase HttpServlet
implementa una serie de métodos que se pueden sobreescribir por parte de una aplicación web con el objetivo de generar contenido propio de forma dinámica. Estos métodos se ejecutan en respuesta a las peticiones HTTP de tipo GET, POST, PUT, DELETE, HEAD, OPTIONS y TRACE, y tienen el apropiado nombre de doGet
, doPost
, doPut
, doDelete
, doHead
, doOptions
y doTrace
. Todos ellos reciben un objeto con la petición realizada desde el cliente y un objeto respuesta donde dejar el resultado.
En las aplicaciones tradicionales sólo se necesita implementar los métodos doGet
y doPost
:
public class HelloServlet extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) { ... @Override public void doPost(HttpServletRequest request, HttpServletResponse response) { ...
El método doHead
es una forma especializada de GET que sólo recibe las cabeceras HTTP. El método doOptions
se utiliza para retornar el tipo de peticiones HTTP que soporta el servlet. Y el método doTrace
retorna por defecto las mismas cabeceras que recibe, lo que resulta útil para depurar algunas opciones avanzadas.
Adicionalmente un servlet puede implementar también el método getLastModified
para responder a peticiones de acceso a recursos de forma eficiente en función de su fecha de última modificación.
Mapping
Los servlets de una aplicación se deben listar en el fichero web.xml para que el servidor web donde se realice el despliegue tenga conocimiento de ellos y los pueda gestionar adecuadamente:
<servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>com.company.servlet.HelloServlet</servlet-class> </servlet>
La etiqueta servlet-name
asigna un nombre al servlet, y la etiqueta servlet-class
indica el nombre completo de la clase que lo implementa.
En una aplicación de escritorio tradicional se utiliza un menú, u otro tipo de control, como botones por ejemplo, para ejecutar las distintas opciones. En una aplicación web en cambio se utiliza un navegador en el cliente y se accede a las distintas opciones a través de distintas URLs. Para completar la definición de un servlet se debe indicar que URLs concretas atiende en el fichero web.xml:
<servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
En el ejemplo, el servidor web invocará al servlet HelloServlet
cuando desde el navegador se intente acceder a la URL «http://<servidor>:<puerto>/<context-root>/hello
«.
El context-root
es habitualmente el nombre de la aplicación. Los servidores web lo toman del nombre del war cuando se despliega. Aunque en la práctica es realmente el nombre del directorio donde se encuentran desplegada la aplicación. Puede ser un tanto confuso a veces, sobre todo porque algunos servidores permiten redefinir el nombre por configuración.
En la configuración se pueden utilizar caracteres comodines («*»), de forma que un mismo servlet puede utilizarse para atender más de una URL. La regla que sigue un servidor web para encontrar el servlet adecuado es buscar primero uno que tenga configurado exactamente la URL pedida. Si no lo encuentra expande las expresiones con comodines, y busca la que case con la URL pedida que tenga el path más largo contando los caracteres separadores «/». Si no lo encuentra busca un servlet que tenga configurada la extensión del fichero de la URL, contando a partir del último «.». Si no lo encuentra utiliza el servlet por defecto, que es aquel que tiene la raíz «/» configurada como URL. Y por último, si no lo encuentra, eleva un error.