viernes, 25 de septiembre de 2009

Configurar NetBeans, Tomcat y un pool de conexiones a PostgreSQL

Este post va dedicado a crear un entorno de desarrollo para aplicaciones web, utilizando el lenguaje Java, la base de datos PostgreSQL y el IDE NetBeans. No me voy a detener en la instalación y configuración de Java ni de PostgreSQL, si no en el entorno en concreto.


PREPARATIVOS
El ejemplo que se ilustra aquí utiliza PostgreSQL 8.3 (http://www.postgresql.org, Java v6u14 (http://java.sun.com), Tomcat 6.0.20 (http://tomcat.apache.org) y NetBeans 6.7.1 (http://www.netbeans.org.

En primer lugar ha de estar instalado el SDK de Java y la base de datos PostgreSQL.


TOMCAT
Ahora, para instalar Tomcat, es preferible descomprimir la versión no instalable, ya que incorpora los archivos catalina.bat y catalina.sh, los cuales son gestionados por NetBeans para lanzar y depurar las aplicaciones. Yo recomiendo, para los usuarios de Windows, instalarlo en el directorio raíz del disco duro, pues en Windows Vista, Windows 7 y Windows Server 2008 utiliza el perfil de usuario para gestiones propias del entorno de sesión, y podría no funcionar.

Una vez instalado Tomcat, acceder al archivo conf/tomcat-users.xml y añadir el usuario administrador y el usuario manager:

<user username="admin" password="admin" roles="tomcat"/>
<user username="manager" password="manager" roles="manager"/>

El siguiente paso es descargarse el driver jdbc de PostgreSQL, el cual puede ser descargado de http://jdbc.postgresql.org. Descargar el driver correspondiente a jdbc3. Una vez descargado el archivo .jar, copiar éste en el directorio lib de Tomcat.


NETBEANS

Configuración del servidor

Una vez instalado NetBeans, hay que agregar el servidor Tomcat. Para ello:
- Menú "Tools" + Opción "Servers"
- Botón "Add Server..." (parte inferior izquierda)
- Seleccionar de la lista "Tomcat 6.0" y botón "Next"
- En el campo "Server Location", hacer clic en el botón "Browse..." y seleccionar la carpeta donde se aloja Tomcat.
- En el campo "Username" ingresar el nombre del usuario "manager"
- En el campo "Password" ingresar la contraseña del usuario "manager"
- Botón "Finish"

El servidor se habrá agregado a la lista de servidores configurados. Al seleccionar este servidor, aparecerán sus propiedades a la derecha. Clic en el botón "Close".


Crear proyecto web

Vamos a crear un proyecto web para configurar el pool de conexiones y mostrar un ejemplo de conexión:
- Menú "File" + opción "New Project..."
- En la lista "Categories" seleccionar "Java Web", y en la lista "Projects" seleccionar "Web Application"
- Botón "Next"
- En el campo "Project Name" dar el nombre del proyecto, por ejemplo "Prueba".
- Botón "Next"
- En el campo "Server" seleccionar "Tomcat 6.0"
- Botón "Finish"


Referencia a la librería de PostgreSQL

Creado el proyecto, lo primero que vamos a hacer es añadir una referencia a la librería de PostgreSQL. Para ello, en el panel "Project" (parte izquierda), aparecerá el proyecto con los diversos elementos del mismo. Hacer clic con el botón derecho sobre "Libraries" y seleccionar "Add JAR/Folder". Navegar hasta el directorio lib de Tomcat y seleccionar el archivo .jar de PostgreSQL. La librería aparecerá en el árbol subyacente.


Configurar pool de conexiones

A continuación vamos a configurar el pool de conexiones. Antes de empezar, comentar que en la versiones recientes de Tomcat, por motivos de seguridad y eficiencia, la configuración de contextos y recursos se realiza por aplicación, y en no en el contexto general de Tomcat. Aclarado ésto vamos a proceder a la configuración.

Desplegar, en el panel de proyecto, el elemento "Web pages" del proyecto. Desplegar ahora el elemento "WEB-INF" y hacer doble clic sobre el archivo "web.xml". Seleccionar ahora el botón "XML" (parte superior de la zona de trabajo), con lo que aparecerá el contenido del archivo. Aquí aparecerá un bloque principal encerrado entre los tags <web-app> y </web-app>. Situarse justo antes del cierre de este bloque y añadir las siguientes líneas:

<resource-ref>
<description>Referencia al pool de conexiones</description>
<res-ref-name>jdbc/[nombreJNDI]</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>

El campo <res-ref-name> contiene el nombre JNDI que se utilizará como contexto para el pool de conexiones.

El siguiente paso es acceder al elemento "Web pages" del proyecto. Desplegar ahora el elemento "META-INF" y hacer doble clic sobre el archivo "context.xml". Se mostrará el contenido:

<Context antiJARLocking="true" path="/TrazalogicOperator"/>

Modificar esta línea para definir dos líneas (apertura y cierre) en lugar de una:

<Context antiJARLocking="true" path="/[NombreProyecto]">
</Context>

[NombreProyecto] es asignado automáticamente por NetBeans, y no es necesario cambiarlo.

Entre estas dos líneas añadir lo siguiente para crear el contexto del pool:

<Resource name="jdbc/[nombreJNDI]"
auth="Container"
type="javax.sql.DataSource"
username="[usuarioPostgreSQL]"
password="[passwordPostgreSQL]"
driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://[servidor]:[puerto]/[nombrebasedatos]"
maxActive="8"
maxIdle="4"/>

Un ejemplo de url sería:

url="jdbc:postgresql://localhost:5432/miBaseDatos"

Si la base de datos va a residir en la misma máquina que Tomcat y la aplicación web, se utilizaría "localhost". Si estuviera en otra máquina, especificar el nombre de la misma (si está configurado en un servidor DNS) o bien la dirección IP de la misma.

El puerto 5432 es el puerto por defecto de la base de datos PostgreSQL. Si se ha configurado otro puerto, modificar este parámetro.


Probar todo

Para probar tanto el servidor de aplicaciones como el pool de conexiones, vamos a crear un Servlet. Para ello:
- Menú "File" + opción "New File..."
- En la lista "Categories" seleccionar "Web", y en la lista "File Type" seleccionar "Servlet".
- Botón "Next"
- En el campo "Class Name" escribir el nombre del servlet, por ejemplo "ServletPrueba".
- En el campo "Package" escribir el nombre del paquete donde se ubicará el servlet, por ejemplo "com.prueba.servlet"
- Botón "Next"
- Botón "Finish"

Se creará el servlet con un código mínimo.

En la sección "import" añadir las siguientes importaciones:

import java.sql.*;
import javax.sql.*;
import javax.naming.*;

Buscar el método processRequest y escribir el siguiente código:

protected void processRequest(HttpServletRequest request, HttpServletResponse response)
 throws ServletException, IOException {
  response.setContentType("text/html;charset=UTF-8");
  PrintWriter out = response.getWriter();
  try {
    out.println("<html>");
    out.println("<head>");
    out.println("<title>Servlet ServletPrueba</title>");
    out.println("</head>");
    out.println("<body>");
    out.println("INICIO...<br>");
    Context initCtx = new InitialContext();
    Context envCtx = (Context) initCtx.lookup("java:comp/env");
    DataSource ds = (DataSource)
      envCtx.lookup("jdbc/[nombreJNDI]");
    Connection conn = ds.getConnection();
    //... Insertar aquí las consultas y actualizaciones a base de datos
    conn.close();
    out.println("...FIN<br>");
    out.println("</html>");
  } catch (Exception ex) {
    out.println("Error: " + ex.toString();
  } finally {
   out.close();
  }
}

A través del nombre JNDI se extrae el contexto para el pool de base de datos, retornando un objeto de tipo DataSource. Una vez obtenido el DataSource, es posible obtener la conexión (método getConnection()). A partir de ahí, con la conexión, procederemos a realizar las consultas a base de datos, o las actualizaciones pertinentes.

El código, tal y como está, servirá para verificar que se obtiene la conexión. Si todo va bien, devolverá una página Web, con el único texto "INICIO... / ...FIN". Si hubiera algún error, retornaría también el error producido.

Para ejecutar este servlet, en el panel de proyecto (parte izquierda), abrir la carpeta "Source Packages" + "com.prueba.servlet", y con el botón derecho hacer clic sobre el elemento "ServletPrueba.java" y seleccionar "Run File" (para ejecutar el servlet) o "Debug File" (para depurar el servlet, previo marcado un punto de interrupción).

Antes de finalizar, comentar qué me ha llevado a escribir este post, tras tres días devanándome la cabeza con Eclipse y con GlassFish, con los que he tenido multitud de problemas y una complejidad bastante elevada en cuanto a una configuración para trabajar. Soy de los que piensan que tenemos ya mucho trabajo por delante como para perder el tiempo descifrando o poniéndote delante de una bola de cristal para intentar explicar cómo configurar algo que debería ser automático y simple, y que en apenas unos segundos y de manera intuitiva debería dejarte empezar a trabajar. Ahí radica el éxito de Visual Studio de Microsoft, que facilita el trabajo multiplicando la productividad.

Con Eclipse, de forma inexplicable, los cambios en mi servlet no se actualizaba ni desplegaba en el servidor. Por más que he intentado saber por qué, aún no me lo explico.

Con GlassFish, que parece un servidor potente y muy bien diseñado, veía muchos pasos para configurar el pool de conexiones (primero una conexión, luego el pool y luego el resource, y luego configurar el context.xml y el web.xml). Pero aún así, mirando la documentaciónd e GlassFish, PostgreSQL no venía homologado en las especificaciones, aunque en realidad sí que permitía su configuración, y la conexión, de hecho me permitía mirar la estructura de la base de datos y ejecutar consultas desde el IDE. Pero a la hora de desarrollar, el DataSource era imposible de capturar, y desde el propio GlassFish, probar el DataSource me daba un error FATAL por que database devolvía null. El problema es que el driver de conexión es peculiar en PostgreSQL, y se obtiene mediante un DataSource "especial" llamado org.postgreSQL.ds.PGSimpleDataSource, el cual es incompatible o no se puede convertir a un DataSource normal. Asimismo, incluso intentando obtener un PGSimpleDataSource en lugar de un DataSource, también daba error porque internamente trabaja con DataSource. Vamos un galimatías que me hizo desistir.

Al final, me empapé la documentación de Tomcat (concretamente http://localhost:8080/docs/jndi-resources-howto.html) y trasteando con Tomcat, conseguí definir este procedimiento de configuración, simplificándolo al máximo y que funciona.

Espero que os sea de utilidad.

Safe Creative #1001195348525