La aplicaci�n Web utilizada para demostrar las tecnolog�as usadas en este tutorial es una simple aplicaci�n de control de una librer�a (solo control y ubicaci�n de los libros, no hay facturaci�n, por ejemplo), llamada Libreria . No es el prop�sito de este tutorial crear una aplicaci�n completa, es s�lo un ejemplo.
Libreria tiene tres clases Java:
- libreria.ContextListener es un oyente del contexto servlet al que Tomcat llamar� cuando inicie y detenga la aplicaci�n Libreria. Esta clase crea un objeto de la clase LibrosBD y lo almacena como un atributo de contexto con el nombre BaseDatos
- libreria.LibrosBD es una clase que representa la base de datos de libros. Su constructor establece una conexi�n a la base de datos (utilizando el objeto LibrosBD que se obtiene del atributo de contexto BaseDatos), y la sesi�n se puede compartir entre varias sesiones web. La clase proporciona varios m�todos de acceso a la base de datos (para insertar, borrar, modificar u obtener registros de la base de datos).
- libreria.Libro es una clase Java que representa un libro. Las p�ginas JSP de la aplicaci�n utilizan esta clase como un Bean contenedor de datos para mostrarlos.
�libreria.ContextListener
libreria.ContextListener es un oyente del contexto servlet al que se llamar� cuando arranque y cuando se pare la aplicaci�n Libreria. Esto se configura en el descriptor de despliegue de la aplicaci�n web.xml. Cuando se arranca la aplicaci�n Libreria se crea un ejemplar de la clase LibrosBD y se almacena como un atributo de contexto; cuando se cierra la aplicaci�n, el objeto LibrosBD se recupera del atributo de contexto y se cierra la conexi�n con la base de datos. Entre tanto, las JSP recuperan el objeto LibrosBD del atributo de contexto siempre que necesitan acceder a la base de datos.
�Prop�sito de la clase ContextListener
El prop�sito de la clase Java libreria.ContextListener es crear un ejemplar de libreria.LibrosBD y almacenarlo como un atributo del contexto servlet identificado como: "BaseDatos". Esto permitir� a las p�ginas JSP recuperar el ejemplar de libreria.LibrosBD y acceder a la tabla Libros.
libreria.ContextListener est� configurado como un oyente de contexto servlet en el fichero web.xml.
�M�todos de la clase ContextListener
-
contextInitialized
Se llama a contextInitialized siempre que la aplicaci�n web arranca.
Primero se recupera el contexto servlet desde el evento de contexto servlet:
ServletContext servletContext = servletContextEvent.getServletContext ();
Luego se crea un ejemplar de LibrosBD y se graba en el contexto servlet identificado como: "BaseDatos". Si este proceso lanza una excepci�n se guarda un log:
try { LibrosBD LibrosBD = new LibrosBD (); servletContext.setAttribute ("BaseDatos", LibrosBD); } catch (Exception e) { servletContext.log ("No se pudo crear el atributo BaseDatos: " + e.getMessage()); } -
contextDestroyed
Se llama a contextDestroyed cuando se cierra la aplicaci�n web.
Primero se recupera el contexto servlet desde el evento ServletContexto:
ServletContext servletContext = servletContextEvent.getServletContext ();
Luego se recupera el objeto LibrosBD desde el contexto y se cierra la base de datos:
LibrosBD LibrosBD = (LibrosBD) servletContext.getAttribute ("BaseDatos"); LibrosBD.close ();Luego se elimina el atributo del contexto servlet:
servletContext.removeAttribute ("BaseDatos");
�C�digo Fuente Completo de la clase ContextListener
//
// ContextListener.java
//
package libreria;
import javax.servlet.*;
public final class ContextListener implements ServletContextListener {
public void contextInitialized (ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.getServletContext ();
try {
LibrosBD LibrosBD = new LibrosBD ();
servletContext.setAttribute ("BaseDatos", LibrosBD);
}
catch (Exception e) {
servletContext.log ("No se pudo crear el atributo BaseDatos: " + e.getMessage ());
}
}
public void contextDestroyed (ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.getServletContext ();
LibrosBD LibrosBD = (LibrosBD) servletContext.getAttribute ("BaseDatos");
LibrosBD.close ();
servletContext.removeAttribute ("BaseDatos");
}
}
�libreria.LibrosBD
libreria.LibrosBD es una clase que representa la base de datos de libros. Su constructor establece una conexi�n a la base de datos, y la sesi�n se puede compartir entre varias sesiones web. La clase proporciona varios m�todos de acceso a la base de datos.
�Prop�sito de la clase LibrosBD
El prop�sito de la clase libreria.LibrosBD es establecer una conexi�n a la base de datos p�blica y proporcionar m�todos para acceder de forma segura y actualizar la tabla Libros contenida en la base de datos sin conflictos con otros usuarios que est�n accediendo a los mismos datos.
S�lo hay una conexi�n a la base de datos que es compartida por todos los usuarios de la aplicaci�n Web Libreria. Esto es adecuado mientras el n�mero de usuarios concurrentes no sea excesivo. Vamos a mantenerlo lo m�s simple posible, s�lo debemos a�adir soporte para varias conexiones si es realmente necesario. Observa, sin embargo, que si vamos a realizar esas complejidades, es en esta clase Java donde debemos hacerlas.
�M�todos de la Clase LibrosBD
- Constructor de LibrosBD:
Establece una conexi�n a la base de datos p�blica.
Hasta hace poco, si quer�amos establecer una conexi�n a una base de datos desde un Servlet bajo Tomcat ten�amos que codificar el driver JDBC, la URL de la base de datos, y el nombre de usuario y su password dentro del Servlet de esta forma:
Class.forName ("org.gjt.mm.mysql.Driver").newInstance (); connection = DriverManager.getConnection ("jdbc:mysql://localhost/MiBase", "mysqlusername", "mysqlpassword");No es buena idea codificar dicha informaci�n en el c�digo fuente de la aplicaci�n. Tomcat 4 resuelve este problema permitiendo al Servlet acceder a Tomcat y recuperar una conexi�n desde el DataSource que se define en el fichero Libreria.xml, como veremos m�s adelante.
Primero se recupera el javax.naming.InitialContext para que se pueda realizar una b�squeda JNDI:
InitialContext initialContext = new InitialContext ();
Despu�s se recupera el contexto de la aplicaci�n Libreria:
Context envContext = (Context) initialContext.lookup ("java:comp/env");Luego se recupera el propio DataSource:
DataSource dataSource = (DataSource) envContext.lookup ("jdbc/biblioteca");Y por �ltimo se recupera la conexi�n desde el DataSource:
this.connection = dataSource.getConnection ();
Todos los par�metros del DataSource tenemos que definirlos en el fichero Libreria.xml (que veremos m�s adelante), aunque tambi�n se pueden modificar usando las facilidades de administraci�n de Tomcat, con la direcci�n http://localhost:8080/admin .
-
getConnection
Se utilizan los m�todos getConnection y releaseConnection para asegurarse de que la conexi�n a la base de datos se puede compartir de forma segura, el recurso de la conexi�n se controla utilizando los m�todos sincronizados getConnection y releaseConnection.
Primero getConnection espera hasta que se libera la conexi�n:
while (this.connectionFree == false) { try { wait (); } catch (InterruptedException e) { } }Luego marca el indicador de conexi�n libre como no libre, as� bloquea la conexi�n hasta que se llame a releaseConnection:
this.connectionFree = false;
Luego se lo notifica a cualquier otro thread de LibrosBD que est� esperando una conexi�n:
notify ();
Finalmente devuelve la conexi�n:
return this.connection;
-
releaseConnection
Primero espera hasta que la conexi�n est� ocupada:
while (this.connectionFree == true) { try { wait (); } catch (InterruptedException e) { } }Luego selecciona el indicador de conexi�n libre a libre, as� desbloquea la conexi�n hasta que se llame a getConnection:
this.connectionFree = true;
Finalmente se lo notifica a cualquier otro thread de LibrosBD que est� esperando una conexi�n:
notify ();
Todas las dem�s rutinas que acceden a la base de datos emplean getConnection y releaseConnection, para que s�lo pueda acceder a la base de datos un thread a la vez.
-
getLibro
El m�todo getLibro toma un par�metro id y devuelve el Libro para ese id; si no se puede encontrar ese id se devuelve un Libro null.
getLibro primero obtiene una conexi�n libre
this.getConnection ();
Luego crea una sentencia select, e inserta el id en la posici�n adecuada:
PreparedStatement preparedStatement = this.connection.prepareStatement ("SELECT * FROM Libros WHERE id = ?"); preparedStatement.setString (1, id);Luego ejecuta la sentencia:
ResultSet resultSet = preparedStatement.executeQuery ();
Si la sentencia select devuelve datos, se ensambla un objeto Libro con los datos, se cierra la consulta sql; se libera la conexi�n se devuelve el objeto Libro:
if (resultSet.next ()) { Libro libro = new Libro ( resultSet.getString (1), resultSet.getString (2), resultSet.getString (3), resultSet.getString (4), resultSet.getString (5), resultSet.getString (6), resultSet.getString (7) ); preparedStatement.close (); this.releaseConnection (); return libro; }Si la sentencia select no devuelve datos, se cierra la sentencia, se libera la conexi�n, y se devuelve un objeto Libro null:
else { preparedStatement.close (); this.releaseConnection (); return null; } -
insertarLibro, borrarLibro, modificarLibro, y obtenerLibros
Todos estos m�todos utilizan una t�cnica similar a la del m�todo getLibro
-
close
close simplemente cierra la conexi�n:
this.connection.close ();
�C�digo Fuente Completo de la clase LibrosBD
//
// LibrosBD.java
//
package libreria;
import java.sql.*;
import java.util.*;
import javax.sql.*;
import javax.naming.*;
public class LibrosBD {
Connection connection;
private boolean connectionFree = true;
private ArrayList libros;
public LibrosBD () throws Exception {
try {
InitialContext initialContext = new InitialContext ();
Context envContext = (Context) initialContext.lookup ("java:comp/env");
DataSource dataSource = (DataSource) envContext.lookup ("jdbc/biblioteca");
this.connection = dataSource.getConnection ();
}
catch (Exception e) {
throw new Exception ("No se pudo abrir la base de datos biblioteca: " + e.getMessage ());
}
}
protected synchronized Connection getConnection () {
while (this.connectionFree == false) {
try {
wait ();
}
catch (InterruptedException e) {
}
}
this.connectionFree = false;
notify ();
return this.connection;
}
protected synchronized void releaseConnection () {
while (this.connectionFree == true) {
try {
wait ();
}
catch (InterruptedException e) {
}
}
this.connectionFree = true;
notify ();
}
public Libro getLibro (String id) {
try {
this.getConnection ();
PreparedStatement preparedStatement = this.connection.prepareStatement
("SELECT id, isbn, editorial, autor, categoria, titulo, ubicacion FROM libros" +
" WHERE id = ?");
preparedStatement.setString (1, id);
ResultSet resultSet = preparedStatement.executeQuery ();
if (resultSet.next ()) {
Libro libro = new Libro (
resultSet.getString (1), resultSet.getString (2), resultSet.getString (3),
resultSet.getString (4), resultSet.getString (5), resultSet.getString (6),
resultSet.getString (7)
);
preparedStatement.close ();
this.releaseConnection ();
return libro;
}
else {
preparedStatement.close ();
this.releaseConnection ();
return null;
}
}
catch (SQLException e) {
this.releaseConnection ();
return null;
}
}
public int insertarLibro (Libro libro) {
int rowsAffected = 0;
try {
this.getConnection ();
PreparedStatement preparedStatement = this.connection.prepareStatement
("INSERT INTO libros (isbn, editorial, autor, categoria, titulo, " +
" ubicacion) VALUES (?, ?, ?, ?, ?, ?)");
preparedStatement.setString (1, libro.getIsbn ());
preparedStatement.setString (2, libro.getEditorial ());
preparedStatement.setString (3, libro.getAutor ());
preparedStatement.setString (4, libro.getCategoria ());
preparedStatement.setString (5, libro.getTitulo ());
preparedStatement.setString (6, libro.getUbicacion ());
rowsAffected = preparedStatement.executeUpdate ();
preparedStatement.close ();
this.releaseConnection ();
}
catch (SQLException e) {
this.releaseConnection ();
return 0;
}
return rowsAffected;
}
public int borrarLibro (String id){
int rowsAffected = 0;
try {
this.getConnection ();
PreparedStatement preparedStatement =
this.connection.prepareStatement ("DELETE FROM libros WHERE id = ?");
preparedStatement.setString (1, id);
rowsAffected = preparedStatement.executeUpdate ();
preparedStatement.close ();
this.releaseConnection ();
}
catch (SQLException e) {
this.releaseConnection ();
return 0;
}
return rowsAffected;
}
public int modificarLibro (Libro libro) {
int rowsAffected = 0;
try {
this.getConnection ();
PreparedStatement preparedStatement =
this.connection.prepareStatement ("UPDATE libros SET isbn=?, editorial=?," +
" autor=?, categoria=?, titulo=?, ubicacion=? WHERE id =?");
preparedStatement.setString (1, libro.getIsbn ());
preparedStatement.setString (2, libro.getEditorial ());
preparedStatement.setString (3, libro.getAutor ());
preparedStatement.setString (4, libro.getCategoria ());
preparedStatement.setString (5, libro.getTitulo ());
preparedStatement.setString (6, libro.getUbicacion ());
preparedStatement.setString (7, libro.getId ());
rowsAffected = preparedStatement.executeUpdate ();
preparedStatement.close ();
this.releaseConnection ();
}
catch (SQLException e) {
this.releaseConnection ();
return 0;
}
return rowsAffected;
}
public Collection getLibros () {
libros = new ArrayList ();
try {
this.getConnection ();
PreparedStatement preparedStatement = this.connection.prepareStatement
("SELECT id, isbn, editorial, autor, categoria, titulo, ubicacion FROM libros");
ResultSet resultSet = preparedStatement.executeQuery ();
while (resultSet.next ()) {
Libro libro = new Libro (
resultSet.getString (1), resultSet.getString (2), resultSet.getString (3),
resultSet.getString (4), resultSet.getString (5), resultSet.getString (6),
resultSet.getString (7)
);
libros.add (libro);
}
preparedStatement.close ();
}
catch (SQLException e) {
return null;
}
this.releaseConnection ();
return libros;
}
public void close () {
try {
this.connection.close ();
}
catch (SQLException e) {
System.out.println (e.getMessage ());
}
}
}
�libreria.Libro
libreria.Libro es una clase Java que representa un libro. Las clases que recolectan datos todav�a son una buena idea cuando se programan JSPs, por eso esta clase es una buena idea para la aplicaci�n Libreria.
El constructor de libreria.Libro almacena los campos del libro en el objeto. Esta clase tambi�n incluye los habituales m�todos get para recuperar los datos del libro:
-
Libro(String id, String isbn, String editorial, String autor, String categoria,
String titulo, String ubicacion) - getId()
- getIsbn()
- getEditorial()
- getAutor()
- getCategoria()
- getTitulo()
- getUbicacion()
�Prop�sito de la clase Libros
El prop�sito de esta clase es recolectar los datos relativos a un libro de nuestra base de datos.
�M�todos de la clase Libros
- Constructor de la clase Libro
El constructor almacena los campos suministrados del libro:
this.id = id; this.isbn = isbn; this.editorial = editorial; this.autor = autor; this.categoria = categoria; this.titulo = titulo; this.ubicacion = ubicacion; -
getId, getIsbn, getEditorial, getAutor, getCategoria, getTitulo y getUbicacion
Estos m�todos devuelven los distintos campos que componente un libro.
�C�digo Fuente Completo de la clase Libro
//
// Libro.java
//
package libreria;
public class Libro {
private String id = null;
private String isbn = null;
private String editorial = null;
private String autor = null;
private String categoria = null;
private String titulo = null;
private String ubicacion = null;
public Libro (String id, String isbn, String editorial,
String autor, String categoria, String titulo, String ubicacion) {
this.id = id;
this.isbn = isbn;
this.editorial = editorial;
this.autor = autor;
this.categoria = categoria;
this.titulo = titulo;
this.ubicacion = ubicacion;
}
public String getId () {
return this.id;
}
public String getIsbn () {
return this.isbn;
}
public String getEditorial () {
return this.editorial;
}
public String getAutor () {
return this.autor;
}
public String getCategoria () {
return this.categoria;
}
public String getTitulo () {
return this.titulo;
}
public String getUbicacion () {
return this.ubicacion;
}
}