Todo servlet hereda de HttpServlet y, al igual que un applet, contiene una serie de métodos predefinidos, que son invocados en respuesta a eventos:
public void init(ServletConfig config) throws ServletException
public void doGet(HttpServletRequest peticion, HttpServletResponse respuesta) throws ServletException, IOException
public void doPost(HttpServletRequest peticion, HttpServletResponse respuesta) throws ServletException, IOException
El objeto 'peticion' es enviado por la JVM del servidor de aplicaciones y representa (encapsula) información de la petición HTTP. Más adelante veremos para que puede servir el objeto 'respuesta', por de pronto nos sirve decir que en este objeto se encapsulan los servicios para generar la salida. También tiene la posibilidad de usar:
public void service(HttpServletRequest peticion, HttpServletResponse respuesta) throws ServletException, IOException
public void destroy()
La primera vez que invocamos al servlet se crea una instancia de la clase en un hilo (thread) de ejecución, se llama a init() e inmediatamente después al método doXX() correspondiente. Las siguientes peticiones generan un hilo independiente, pero no una nueva instancia. Veamos un ejemplo, supongamos que un servlet tiene el atributo:
private int contador = 0;
y al principio del método doXX() se incrementa:
contador++;
La primera invocación al servlet implica que se crea la instancia y el valor del atributo será uno. En la siguiente invocación (en su hilo independiente) no se genera una instancia, por lo que el valor del atributo se vuelve a incrementar y así sucesivamente. Pruebe a hacerlo y visualizar el valor de la variable. Verá que todas las peticiones (invocaciones) al servlet comparten los mismos datos (las variables atributo). El lector se puede plantear que hay situaciones en las que nos interesa que la sesión mantenga datos propios, no compartidos con otras sesiones. Por ejemplo, cuando estamos en una web transaccional (una tienda electrónica) queremos que cada sesión pueda utilizar datos específicos, como la identificación de usuario o el carrito de la compra. Esto se puede hacer (además de usando una base de datos) manejando información específica a la sesión, pero esto es asunto de estudio posterior.
Invoca al servlet de ejemplo.
También tienes acceso al código fuente.
Veamos que se puede hacer con init():
m.append("Servlet '"+config.getServletName()+"' arrancado a las " + new Date() );
m.append( "<OL>");
for ( Enumeration e= config.getInitParameterNames(); e.hasMoreElements(); ) {
String nom_par = (String) e.nextElement(); // Obtiene nombre del parámetro
m.append("<LI>Parámetro de ServletConfig: " + nom_par);
m.append(". Valor: " + config.getInitParameter( nom_par ) ); // Obtiene valor del parametro
}
m.append( "</OL>");
En nuestro caso, posteriormente (doPost()/doGet()), obtendremos en la visualización lo siguiente:
1. Parámetro de ServletConfig: CURSO. Valor: SERVLETS
La razón es que hemos puesto en web.xml los siguientes parámetros de inicio:
<servlet>
<servlet-name>Metodos</servlet-name>
<servlet-class>docen_servlet01.Metodos</servlet-class>
<init-param>
<param-name>CURSO</param-name>
<param-value>SERVLETS</param-value>
</init-param>
</servlet>
ServletContext sc = config.getServletContext();La información que se requiere a partir de aquí es servida por el objeto sc de la clase ServletContext. De los siguientes atributos de contexto el más interesante tal vez sea getRealPath("/"), que nos devuelve el directorio raíz del contexto de aplicación:
m.append( "<P>RealPath: " + sc.getRealPath( "/") );
m.append( "<P>ServerInfo: " + sc.getServerInfo() );// Nombre del servidor web
m.append( "<P>Versión API Servlet: " + sc.getMajorVersion() + "." +sc.getMinorVersion() ); //API
Observar la diferencia en J2EE entre servlet y aplicacióin. Nuestro servlet está en
ROOT\WEB-INF\classes, dentro de la aplicación ROOT. La aplicación engloba a una serie de
servlets entre los que está el nuestro. La diferencia entre servlet y aplicación
es la que existe entre un contenido y un contenedor.
m.append( "<OL>");
for ( Enumeration e= sc.getAttributeNames(); e.hasMoreElements(); ) {
String nom_par = (String) e.nextElement(); // Obtenemos el nombre del atrib
m.append("<LI>Atributo de contexto: " + nom_par);
m.append(". Valor: " + sc.getAttribute( nom_par ) ); // Se obtiene el valor del atrib
}
m.append( "</OL>");
sc.log( "----> Inicio de servlet Metodos" ); // Salida a archivo LOG del serv de apps
En el código fuente se puede observar que toda esta información se vuelca en StringBuffer m. La finalidad de almacenarla es que luego (en el método doGet) podremos obtenerla para imprimir los datos.
Nuestro método doGet() reenvia la petición a doPost():
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
En doPost() la forma de presentar la información de inicio que ha sido almacenada en el StringBuffer es muy sencilla:
out.println( m );
A continuación mostramos los atributos del sistema (System). Lo primero es obtener todas las propiedades por medio de System.getProperties(). En segundo lugar obtenemos una Enumeration de las claves (nombres de propiedades):
Properties p = System.getProperties();
Enumeration enum= p.keys();
Obtener los valores es tan sencillo como llamar a getProperty() para cada clave:
for (Enumeration enum= p.keys(); enum.hasMoreElements(); ) {
String clave = (String) enum.nextElement();
out.println( "<TR><TD>CLAVE: " + clave + "</TD>" );
out.println( "<TD>" + p.getProperty(clave) + "</TD></TR>" );
}
La función destroy() del servlet se ejecuta cuando éste va a ser destruido (normalmente por la detención del servidor). No se invoca cuando se termina la petición, sino cuando se va a descargar el servlet de memoria.
El programador debe liberar o cerrar recursos, como conexiones o flujos de entrada/salida. Ante esto, surge un interrogante, en peticiones de larga resolución pudiera ocurrir que se liberen recursos que todavía son necesarios. Supongamos que hay un usuario que esta comprando o transfiriendo dinero y en estos momento la petición está en doPost(), si produjesemos una desconexión el usuario se quedaría a medias. Para evitar esto el servidor tiene dos estrategias: no ejecuta destroy() hasta que han terminado las llamadas a las funciones de respuesta (doGet, doPost y service) o, en segundo lugar, hasta que termine un "periodo de gracia".
Para más información: Tutorial de Sun