![]() |
Curso
JAVA |
|
www.bit.es - Calendario de cursos - Solicitud de información |
Curso
Java
Curso Analista Programador
entorno Java
Objetivos de la Unidad:
Una muy interesante referencia de Tomcat
en castellano se puede consultar en http://www.programacion.com/java/tutorial/tomcatintro y
sobre servlets/JSP en http://www.programacion.com/java/tutorial/servlets_jsp
Muy bueno también pero en inglés tenemos http://www.coreservlets.com/Apache-Tomcat-Tutorial
El
tutorial de Sun cubre una buena introducción a los servlets en http://java.sun.com/javaee/reference/tutorials/index.jsp.
Ejemplos potentes se pueden encontrar en http://www.servlets.com/jsp/examples/
Los JSP permiten crear "custom
Tags". Unas buena referencias son http://developer.java.sun.com/developer/Books/cservletsjsp/chapter14.pdf y http://developer.java.sun.com/developer/technicalArticles/xml/WebAppDev3
(custom.war)
Para una introducción en castellano
puede leerse los apuntes de la Escuela de Ingenieros de Navarra en http://mec21.etsii.upm.es/ayudainf/aprendainf/JavaServlets/servlets.pdf y el artículo publicado
en la revista Sólo Programadores http://www.towercom.es/nsp0002.html.
Una variante basada en servlets
son las JSP (Java Server Pages) de las que se puede leer una introducción en http://developer.java.sun.com/developer/technicalArticles/Programming/jsp/ , un tutorial en http://java.sun.com/products/jsp/docs.html y una carta sintáctica simplificada en
jsp_syntax.pdf y una completa en en http://java.sun.com/products/jsp/syntax/2.0/card20.pdf
Los servlets y JSP se ejecutan dentro de
un Web Container como el de IBM WebSphere, iPlanet, Tomcat, Oracle, Bea Systems,
etc. La configuración de cualquiera de ellos es estándar y una buena documentación
es la de Bea en http://edocs.bea.com/wls/docs61/webapp/web_xml.html
Tras un acuerdo de Sun Microsystems
y grupo Open Source Apache el entorno de desarrollo de referencia pasa a ser Tomcat.
Este servidor puede funcionar tanto de plug-in de Apache e IIS así como
stand-alone. La calidad del producto supera a la de un kit de desarrollo y puede
ser utilizado como servidor base de aplicaciones reales. Es gratuito y de código
fuente abierto. Las direcciones de referencia de Sun y Apache son respectivamente
http://java.sun.com/products/jsp/download.html y http://jakarta.apache.org/tomcat/index.html
Para configurarlos en un IDE (como JBuilder
ó Visual Age) pueden seguirse los pasos indicados aquí.
Tomcat puede funcionar como stand-alone
o como un plugin de Apache o IIS. La configuración para incrustar Tomcat en el
IIS de Microsoft se puede consultar en http://tomcat.apache.org/connectors-doc/config/iis.html y la de apache en http://tomcat.apache.org/connectors-doc/config/apache.html
El servidor Web Apache es el más utilizado actualmente
en Internet y soporta servlets tanto para UNIX, LINUX y Win32 mediante el proyecto
Java Apache Project
Plataformas que soportan servlets:
http://www.servlets.com/engines
http://jserv.javasoft.com/products/java-server/servlets/environments.html
Otras
fuentes:
http://www.servlets.com
http://en.wikipedia.org/wiki/Java_Servlet
Herramientas para el desarrollo de servlets:
Como ejercicio del curso se propone un ServletWizard (Wizard.zip) del cual en la página principal se hace referencia puesto que se publicó en relación a él un artículo en la revista "Sólo Programadores"
Una
tecnología importante en grandes instalaciones es la denominada EJB (Enterprise
Java Beans). Puede leerse una introducción en http://www.proactiva-calidad.com/java/ejb/index.html
y más información en http://java.sun.com/products/ejb/
Las Collections (http://developer.java.sun.com/developer/technicalArticles/Collections/Using/index.html) son una novedad de Java2 muy útil para
la gestión de la persistencia de objetos complejos en el marco de los EJB
Los EJB son la base de los servidores de aplicaciones J2EE basados en Java como
i.Planet, IBM WebSphere, Bea WebLogic, Oracle, IONA, JBoss, etc. que cuentan con
herramientas como EJBWizard
Un importante site de referencia para
foros e información de todo tipo sobre J2EE es http://www.theserverside.com
Una
exhaustiva lista de servidores de aplicaciones, tanto gratuitos como de pago puede
encontrarse en http://java.sun.com/j2ee/licensees.html
Para ver un ejemplo de cómo se lleva a cabo el deployment en un servidor
de aplicaciones (en este caso J2EE de Sun) puede analizarse el contenido de los
ficheros resultantes .ear y .jar
de su asistente de deployment.
Aunque la oferta de hosting de Servlets & JSP es menor que la oferta de LAMP hosting (Linux + Apache + MySQL + PHP) pueden encontrarse proveedores como www.praktonhost.com que ofrecen servlet & JSP hosting con MySQL sobre Linux a precios LAMP en el Datacenter de Colt Telecom de Barcelona. Colt Telecom es una referncia de primer nivel en cuanto a potencia y calidad de
sus Datacenters (www.sun.com/customers/software/colt.xml). Una
lista de proveedores de Servlet/JSP hosting puede encontrarse en http://resources.corejsp.com/jsp-hosting.html
y en http://www.servlets.com/isps/servlet/ISPViewAll.
Servlet
mínimo
import
javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class HolaServlet extends HttpServlet
{
public void
service (HttpServletRequest req, HttpServletResponse res) throws
ServletException,
IOException
{
PrintWriter p = res.getWriter();
res.setContentType("text/html");
p.println("<html>");
p.println("<body>");
p.println("<h2>Hola Mundo</h2>");
p.println("</body>");
p.println("</html>");
p.close();
}
}
Estructura de un Servlet básico
//
//
HolaServlet.java
//
// Para probarlo hay que arrancar el servidor (En este caso ejecutar Aplicacion)
// luego ir a un navegador y solicitar la siguiente URL:
//
// Para
probar el servlet hay que ir a un navegador y pedir la siguiente URL:
// http://[dirección
ip]:[port]/[contexto]/servlet/[package].[clase servlet]
//
// En este caso teniendo servidor y navegador en la misma máquina puede
probarse con:
// http://localhost:8080/servlet/HolaServlet
//
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class HolaServlet extends HttpServlet
{
public
void init(ServletConfig config) throws ServletException
{
super.init(config);
// inicialización
del servlet al cargarse en memoria
}
public
void service (HttpServletRequest req, HttpServletResponse res) throws
ServletException,
IOException
{
PrintWriter p = res.getWriter();
res.setContentType("text/html");
p.println("<html>");
p.println("<body>");
p.println("<h2>Hola Mundo</h2>");
p.println("</body>");
p.println("</html>");
p.close();
}
public
void destroy()
{
// liberación de
recursos al descargarse de memoria
}
}
//
//
Aplicacion.java
//
// Nota: Para arrancar dentro de JBuilder en debug hay que indicar en Run-VM parameters
lo siguiente:
// -Dtomcat.home=c:\jakarta-tomcat-3.2.3
(para Tomcat 3)
// -Dcatalina.home="c:\Archivos
de programa\Apache Tomcat 4.0" (para Tomcat 4)
public
class Aplicacion
{
public static void main(String[] args)
{
// API de servlets 2.0 con el JSDK 2.0
//sun.servlet.http.HttpServer.main(args);
// API de servlets 2.1 con el JSWDK
//
com.sun.web.shell.Startup.main(args);
// Tomcat 3.2.3
// org.apache.tomcat.startup.Tomcat.main(args);
// Tomcat 4
org.apache.catalina.startup.Bootstrap.main(new
String[]{"start"});
}
}
El
método init() se ejecuta la primera vez que llega un impacto. Es similar al init
de un Applet. Una aplicación típica de init() es establecer sesión con una base
de datos, así como establecer parámetros iniciales. Ver la documentación de ServletConfig.
public
class ContadorServlet extends HttpServlet
{
int contadorAplicacion
= 0;
public void service(HttpServletRequest req, HttpServletResponse res)
throws IOException, ServletException
{
PrintWriter
p = new PrintWriter(res.getOutputStream());
res.setContentType("text/html");
int contadorRequest = 0; // Gestión del contador
de sesión:
int contadorSesion = 1;
HttpSession
ses = req.getSession();
Integer contador = (Integer)ses.getAttribute("contador");
if (contador != null)
{
contadorSesion = contador.intValue();
contadorSesion++;
}
ses.setAttribute("contador", new Integer(contadorSesion));
// incrementos de los contadores de aplicacion y request:
contadorAplicacion++;
contadorRequest++;
// Muestra de los valores de los contadores:
p.println("<HTML>");
p.println("<BODY>");
p.println("<BR><BR>");
p.println("Contador a nivel de aplicación:
" + contadorAplicacion + "<BR>");
p.println("Contador
a nivel de sesión:" + contadorSesion + "<BR>");
p.println("Contador a nivel de request: "
+ contadorRequest + "<BR>");
p.println("</BODY>");
p.println("</HTML>");
p.close();
}
}
Ejemplos:
Servlet Wizard y JSP
Como ejercicio del curso se propone un ServletWizard (Wizard.zip) del cual en la página principal
se hace referencia puesto que se publicó en relación a él un artículo en la revista
"Sólo Programadores"
En el archivo ServletsJSP.zip se encuentra una demo con el código fuente con ejemplos probables de JSP básicos así como una muestra de interrelacionar servlets con JSP.
3.- JSPs.
Los
JSP plantean una estrategia opuesta a los servlets: en lugar de ser Java que genera
HTML se trata de HTML en el que se incrusta JAVA. La ventaja es que los ficheros
JSP (denominados páginas JSP) pueden ser editados con una herramienta visual como
Dreamweaver, FrontPage ó similar. Tecnológicamente se crea un servlet automáticamente,
se compila (sólo la primera vez) y se ejecuta para cada JSP.
<%@
page import="java.util.*, java.io.*" %>
<%!
public void jspInit()
{
// Este método
equivale al init() de un servlet
}
public void jspDestroy()
{
}
%>
<% Rectangulo r = new Rectangulo(0, 0, 10, 5); %>
<html>
<body>
Hola Mundo
La base del rectangulo es <%=
r.getBase() %> y la altura es <%= r.getAltura() %>.
</body>
</html>
La sintaxis de los JSP se encuentra resumida en http://java.sun.com/products/jsp/syntax.pdf
4.- Acceso de bases de datos. JDBC.
Como documentación de partida una de las mejores opciones es ir al tutorial de Sun en http://java.sun.com/docs/books/tutorial/jdbc/basics/index.html y cómo no en las FAQ sobre JDBC en http://java.sun.com/products/jdbc/faq.html. También puede seguirse un curso on-line en http://developer.java.sun.com/developer/onlineTraining/Database/JDBCShortCourse/index.html
Desde
un punto de vista más amplio la página de referencia es http://java.sun.com/products/jdbc
En
relación con el acceso a bases de datos en AS/400 puede ser interesante empezar
por las FAQ al respecto en http://www.jguru.com/faq/Java400.
Se puede aumentar sustancialmente
el rendimiento con un pool de conexiones tal y como se indica en http://www.web400.com/download/JDBCseminar.html.
Para ello se puede utilizar el AS/400 Java toolbox http://www.ibm.com/as400/toolbox
(JTOpen)
Una de las bases de
datos más reputadas en el mundo Java es Oracle. Puede descargarse una versión
gratuita denominada Oracle Express en http://www.oracle.com/technology/software/products/database/xe/index.html.
Microsoft ofrece una versión reducida y gratuita del SQLserver denominada SQL
Server Express (antes MSDE) que se puede obtener en http://msdn.microsoft.com/vstudio/express/sql/download.
Hay 4 tipos de drivers JDBC: Tipo 1, tipo 2, tipo 3 y tipo 4 según se describe en http://www.javaworld.com/javaworld/jw-07-2000/jw-0707-jdbc.html y un buen índice de drivers disponibles puede encontrarse en http://industry.java.sun.com/products/jdbc/drivers y en http://ourworld.compuserve.com/homepages/Ken_North/jdbcvend.htm
Los drivers tipo 3 y 4 permiten acceso directo desde el driver 100% Java a la base de datos utilizando protocolos que van por encima del transporte. Es el caso de SQL*Net de Oracle o de TDS de SQL Server. Sin embargo su utilización en arquitecturas de dos capas puede chocar con paso por los firewalls tal y como se expone en http://www.dbmsmag.com/9802d13.html
Los drivers de MySQL pueden bajarse de http://www.mysql.com/products/connector/j.
Los drivers de Postgresql (http://www.ejip.net/faq/postgresql_win_setup_faq.jsp) pueden bajarse de http://jdbc.postgresql.org/
Drivers y cadenas de conexión JDBC:
| Fabricante | Driver | JDBC URL |
| Oracle Express | oracle.jdbc.driver.OracleDriver | jdbc:oracle:thin:@//localhost:1521/XE |
| Oracle general | oracle.jdbc.driver.OracleDriver | jdbc:oracle:thin:user/password@host:port:database |
| Oracle internal JVM | No hace falta Class.forName (Internal JVM) | jdbc:default:connection |
| MySQL | com.mysql.jdbc.Driver | jdbc:mysql://host:port/database |
| MySQL (Old) | org.gjt.mm.mysql.Driver | jdbc:mysql://host:port/database |
| SQL Server | com.internetcds.jdbc.tds.Driver | jdbc:freetds:sqlserver://host:1433/database |
| SQL Server MS | com.microsoft.jdbc.sqlserver.SQLServerDriver | jdbc:microsoft:sqlserver://host:port\\instance ;DatabaseName=database |
| AS/400 | com.ibm.db2.jdbc.app.DB2Driver | jdbc:db2:PID400H |
| Postgresql | org.postgresql.Driver | jdbc:postgresql://host:port/database |
| ODBC | sun.jdbc.odbc.JdbcOdbcDriver | jdbc:odbc:odbc_dsn |
| ODBC (MS) | com.ms.jdbc.odbc.JdbcOdbcDriver | jdbc:odbc:odbc_dsn |
Otros: http://www.ewin.org/~bret/java/jdbc/drivers.html
La gestión de excepciones es clave para garantizar un buen rendimiento en las aplicaciones (http://websphere.sys-con.com/read/43087.htm)
5.- SQL
Un
tutorial con ejemplos resueltos se puede encontrar en http://www.w3schools.com/sql/default.asp.
En cuanto al SQL del MySQL http://dev.mysql.com/doc/refman/5.0/es/index.html
es la referencia introductoria. A nivel de especificaciones y estándares
http://www.jcc.com/sql.htm y www.wiscorp.com/SQLStandards.html son buenos puntos de información.
6.- Ejemplo JDBC - Java - SQL:
Para
que funcione en un PC hay que definir la entrada ODBC denominada Neptuno en Panel
de Control->ODBC 32 bits.
Este
ejemplo funciona stand-alone. Queda como ejercicio para el elector adaptarlo a
un servlet y generar HTML dinámicamente con los datos ontenidos de la base de
datos.
//
//
ProvaJDBC.java
//
import
java.sql.*;
import
java.util.*;
public
class ProvaJDBC
{
public static void main(String args[]) throws Exception
{
Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");
Connection con = DriverManager.getConnection("jdbc:odbc:manart",
"admin", "");
Statement
stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM Articulos");
while (rs.next())
{
System.out.println(rs.getString(1) + " - " +
rs.getString(2)
+ " - " + rs.getString(3));
}
rs.close();
stmt.close();
con.close();
}
}
También se pueden utilizar arrays asociativos:
//
//
ProvaJDBC.java
//
import
java.sql.*;
import
java.util.*;
public
class ProvaJDBC
{
public static void main(String args[]) throws Exception
{
Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");
Connection con = DriverManager.getConnection("jdbc:odbc:manart", "admin",
"");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM Articulos");
while (rs.next())
{
System.out.println(rs.getString("articuloId") + " - " +
rs.getString("descripcionArticulo")
+ " - " + rs.getString("precioArticulo"));
}
rs.close();
stmt.close();
con.close();
}
}
El ejemplo anterior se puede adaptar a un servlet:
import
javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import
java.sql.*;
public class HolaServlet extends HttpServlet
{
public
void service (HttpServletRequest req, HttpServletResponse res) throws
ServletException,
IOException
{
try
{
PrintWriter p = res.getWriter();
res.setContentType("text/html");
p.println("<html>");
p.println("<body>");
p.println("<pre>");
Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");
Connection con = DriverManager.getConnection("jdbc:odbc:manart", "admin",
"");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM Articulos");
while (rs.next())
{
p.println(rs.getString(1) + " - " +
rs.getString(2)
+ " - " + rs.getString(3));
}
rs.close();
stmt.close();
con.close();
p.println("</pre>");
p.println("</body>");
p.println("</html>");
p.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
public void destroy()
{
//
liberación de recursos al descargarse de memoria
}
}
El ejemplo anterior se puede adaptar a un JSP (pej: provajdbc.jsp):
<%@
page import="java.io.*,java.sql.*" %>
<html>
<body>
<h3>Listado<h3>
<table border="1">
<%
Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");
Connection con = DriverManager.getConnection("jdbc:odbc:manart", "admin",
"");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM Articulos");
while (rs.next()) {
%>
<tr><td><%= rs.getString(1) %></td><td><%= rs.getString(2)
%></td><td><%= rs.getString(3) %></td></tr>
<%
}
rs.close();
stmt.close();
con.close();
%>
</table>
</body>
</html>
Este estilo de programación directa en el JSP no es tan utilizada en el colectivo
JAVA como en los colectivos PHP o ASP. Se puede lograr un nivel de encapsulación
no orientada a objetos con la directiva <%@
include que es una
instrucción de pre-procesador que permite reutilizar código por inclusión y que
luego se compila conjuntamente. Los scriplets con <%
se incluyen a nivel
del método service del JSP subyacente y los <%!
se incluyen a nivel
de clase por lo que se pueden definir rutinas que pueden ser incluidas en varios
JSP.
En http://www.ejip.net/ejip.jsp
se ofrece espacio de pruebas gratuito de JSP.
El ejemplo anterior se
organizar por inclusión de código directo (codigo.jsp) o de rutinas (libreria.jsp)
en pej: provajdbc2.jsp:
<!--
provajdbc2.jsp -->
<%@ page import="java.io.*,java.sql.*"
%>
<%@ include file="codigo.jsp" %>
<%@ include
file="libreria.jsp" %>
<html>
<body>
<h3>Listado<h3>
<table border="1">
<%
while (rs.next()) {
%>
<tr><td><%= rs.getString(1) %></td><td><%= rs.getString(2)
%></td><td><%= rs.getString(3) %></td></tr>
<%
}
rs.close();
stmt.close();
con.close();
%>
</table>
<%= despedida() %>
</body>
</html>
<!-- codigo.jsp
-->
<%
Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");
Connection con = DriverManager.getConnection("jdbc:odbc:manart", "admin",
"");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM Articulos");
%>
<!--
libreria.jsp -->
<%!
String despedida()
{
return
"Adiós: Texto enviado desde una rutina incluida";
}
%>
Sin
embargo al disponer Java de clases se puede (y se recomienda para proyectos
complejos) encapsular la lógica de acceso en ellas y no mezclarla con el JSP.
Se trata de separar la capa de presentación de la lógica de negocio en capas distintas.
Almacenamiento
de imágenes y otra información en bases de datos.
Se adjunta a continuación
en img.zip
un ejemplo completo de cómo:
Un
ejemplo detallado y paso a paso de un mantenimiento de pedidos con JDBC (maqueta
sobre MS-Acess) y Swing (con JTable) se puede encontrar en http://developer.java.sun.com/developer/technicalArticles/Database/dukesbakery
El ejemplo funcionante
de servlet para realizar uploads (upload.war)
puede ser de gran utilidad para llevar contenidos al servidor.
MetaData
JDBC:
A continuación se muestra un ejemplo de los servicios "metadata"
que permiten obtener dinámicamente información sobre las tablas, vistas, etc.
En este caso se muestran los 100 primeros registros de una tabla con sólo indicar
el nombre de la tabla:
//
//
PruebaMetadataJDBC.java
//
import
java.sql.*;
import java.util.*;
import java.io.*;
public class
PruebaMetadataJDBC
{
public static void
main(String args[]) throws Exception
{
Connection con = null;
try
{
Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");
String dsn = "jdbc:odbc:Neptuno";
String user = "admin";
String password = "";
con = DriverManager.getConnection(dsn, user, password);
}
catch( Exception ee)
{
ee.printStackTrace();
}
// Metadatos
a nivel de base de datos: Tablas y vistas disponibles, etc
DatabaseMetaData
dmd = con.getMetaData();
// Muestra
las tablas y vistas que empiezan por c
ResultSet
rsd = dmd.getTables(null, null, "c%", null);
System.out.println("------
tablas y vistas que empiezan por c -------");
while
(rsd.next())
{
System.out.println(rsd.getString(3));
}
String
tipos[] = {"TABLE"};
rsd = dmd.getTables(null,
null, null, tipos);
System.out.println("------
sólo tablas -------");
while
(rsd.next())
{
System.out.println(rsd.getString(3));
}
//
Metadatos a nivel de tabla: nombre y tipos de columnas de una tabla o vista, etc
Statement stmt; // SQL statement object
String query; // SQL select string
ResultSet rs; // SQL query results
String v1, v2; // Temporary storage results
String whereClause = "";
query = "SELECT * "
+ "FROM Empleados "
+ whereClause;
try
{
stmt = con.createStatement();
rs = stmt.executeQuery(query);
ResultSetMetaData rsmd = rs.getMetaData();
int numCols = rsmd.getColumnCount();
PrintStream p = System.out;
System.out.println("");
System.out.println("--
Estructura la tabla empleados y a continuación sus datos --");
for (int i = 1; i <= numCols; i++)
{
p.print(" ");
String columnNom = rsmd.getColumnName(i);
int
columnTipo = rsmd.getColumnType(i);
p.print(columnNom
+ " (tipo " + columnTipo + ")");
p.print(" ");
}
System.out.println("");
while (rs.next())
{
for (int i = 1; i <= numCols; i++)
{
p.print("
");
int
columnTipo = rsmd.getColumnType(i);
if
(columnTipo == -4) // campo de foto
{
p.print("[foto]");
}
else
{
String
columnVal = rs.getString(i);
p.print(columnVal);
}
p.print("
");
}
System.out.println("");
}
rs.close();
stmt.close();
}
catch (SQLException e)
{
e.printStackTrace();
}
}
}
Una variante del ejemplo anterior nos permitirá generar una clase Java a partir del la información de estructura de una base de datos.
import java.io.*;
import java.sql.*;
public class Db2class
{
public static void main(String[] args) throws Exception
{
DataInputStream d = new DataInputStream(System.in);
System.out.print("Tabla: ");
String
tabla = d.readLine();
Connection con = null;
try
{
Class.forName
("sun.jdbc.odbc.JdbcOdbcDriver");
String
dsn = "jdbc:odbc:Neptuno";
String
user = "admin";
String password
= "";
con = DriverManager.getConnection(dsn,
user, password);
}
catch(
Exception ee)
{
ee.printStackTrace();
}
Statement stmt;
String query;
ResultSet rs;
query = "SELECT * " + "FROM "
+ tabla;
try
{
stmt = con.createStatement();
rs
= stmt.executeQuery(query);
ResultSetMetaData
rsmd = rs.getMetaData();
int numCols
= rsmd.getColumnCount();
PrintStream p
= System.out;
FileOutputStream f
= new FileOutputStream(tabla + ".txt");
PrintStream
pf = new PrintStream(f);
pf.println("");
pf.println("");
pf.println("public
class " + tabla);
pf.println("{");
for (int i = 1; i <= numCols; i++)
{
String
columnNom = rsmd.getColumnName(i);
String
tipo = sqlTipo2JavaTipo(rsmd.getColumnTypeName(i));
pf.println("
private " + tipo + " " + columnNom + ";");
p.print(columnNom);
p.print(" ");
}
pf.println("");
pf.println("");
for (int i = 1; i <= numCols; i++)
{
String
columnNom = rsmd.getColumnName(i);
String
tipo = sqlTipo2JavaTipo(rsmd.getColumnTypeName(i));
pf.println("
public void set" + primMayusc(columnNom) + "(" + tipo + "
valor)");
pf.println("
{");
pf.println("
" + columnNom + " = valor;");
pf.println("
}");
pf.println("");
pf.println(" public
" + tipo + " get" + primMayusc(columnNom) + "()");
pf.println(" {");
pf.println(" return "
+ columnNom + ";");
pf.println("
}");
pf.println("");
}
pf.println("}");
System.out.println("");
rs.close();
stmt.close();
}
catch (SQLException e)
{
e.printStackTrace();
}
System.out.println("Se
ha generado " + tabla + ".txt Pulse enter para cerrar");
}
static String sqlTipo2JavaTipo(String sqlTipo)
{
if (sqlTipo.equals("VARCHAR"))
return
"String";
else if (sqlTipo.equals("INTEGER"))
return "int";
else
if (sqlTipo.equals("DATETIME"))
return
"Date";
else if (sqlTipo.equals("LONGBINARY"))
return "Object";
else
if (sqlTipo.equals("LONGCHAR"))
return
"String";
else if (sqlTipo.equals("COUNTER"))
return "int";
else
if (sqlTipo.equals("DOUBLE"))
return
"double";
else
return
"Object";
}
public
static String primMayusc(String s)
{
String
primLetra = s.substring(0, 1);
return primLetra.toUpperCase()
+ s.substring(1);
}
}
Una
versión bastante más completa que incluye la generación de código inspirado en
los Enterprise Java Beans (EJB) se puede encontrar en Db2class.java.
En la práctica supone disponer de un sistema de persistencia básico automatizado.
Sobre esquemas de persistencia la obra de Scott Ambler "Mapping
Objects to Relational Databases (ORM) " (http://www.ambysoft.com/mappingObjects.html)
es toda una referencia. Son varias las implementaciones Java basadas en sus ideas como Hibernate (www.hibernate.org) o Ibatis (www.ibatis.org) y anteriormente otros frameworks como http://abra.sourceforge.net/
o http://osage.sourceforge.net/.
En general una buena referencia sobre Patrones Objeto/Relacional
se puede encontrar en http://www.objectarchitects.de/ObjectArchitects/orpatterns/
J2EE
(Java 2 Enterprise Edition - http://java.sun.com/j2ee)
propone un modelo de persistencia con los Entity Beans. Una referencia rápida
de J2EE clásicos está en los fichero http://www.theserverside.com/resources/pdf/ejbmatrix11.pdf
y en http://www.theserverside.com/resources/pdf/EJB-Matrix-2.0-Draf2.pdf.
Parece que en un futuro el estándar definitivo será JPA (java.sun.com/developer/technicalArticles/J2EE/jpa y www.eclipse.org/webtools/dali/main.php) que acabará fagocitando a los EJBs, Hibernate o Ibatis. Por otra parte hasta la fecha ha gozado de gran popularidad el uso del patrón DAO (en.wikipedia.org/wiki/Data_Access_Object) mediante generadores de código (titaniclinux.net/daogen). Interesante la discusión sobre la controversia de usar ORM versus DAO en www.codefutures.com/weblog/andygrove/archives/2005/02/data_access_obj.html.
En entornos visuales es habitual
disponer de adaptadores de conexión JDBC (www.sterlingcommerce.com/Documentation/GIS40/Content/SAAE/Java%20Database%20Connectivity%20(JDBC)%20Adapter.html). Vamos a crear uno muy simple denominado BDAdaptorBean que no sólo valdrá
para conexiones ODBC sino para cualquier conexión JDBC. Ciertos entornos gestionan beans invisibles. En caso de no hacerlo
un truco sería hacer que BDAdaptorBean derivara de Panel y darle un aspecto visual
en modo diseño pero no hacerlo visible en modo ejecución. Esto permite personalizar
las propiedades origenDeDatos, driver, username, password, tabla y whereClause
mediante la ventana de propiedades del entorno visual.
import
java.sql.*;
import java.util.*;
public class BDAdaptorBean
{
private Connection con = null;
private String classDriver
= "";
private String origenDatos = "";
private String username = "";
private String
password = "";
private String tabla = "";
private String campos = "";
private String
whereClause = "";
private String query = "";
public BDAdaptorBean()
{
this("jdbc:odbc:Neptuno");
}
public BDAdaptorBean(String valor)
{
// Valores por defecto para prueba con
MS-Access
setOrigenDatos(valor);
setDriver("sun.jdbc.odbc.JdbcOdbcDriver");
setUsername("Admin");
setPassword("");
}
public void setDriver(String valor)
{
classDriver = valor;
}
public String getDriver()
{
return
classDriver;
}
public void setOrigenDatos(String
valor)
{
origenDatos = valor;
}
public String getOrigenDatos()
{
return origenDatos;
}
public void setUsername(String valor)
{
username = valor;
}
public
String getUsername()
{
return username;
}
public void setPassword(String
valor)
{
password = valor;
}
public String getPassword()
{
return password;
}
public void conectar() throws ClassNotFoundException, SQLException
{
Class.forName (classDriver);
String
dsn = origenDatos;
con = DriverManager.getConnection(dsn,
username, password);
}
public
void setConnection(Connection con)
{
this.con
= con;
}
public Connection getConnection()
{
return con;
}
public void close() throws SQLException
{
con.close();
}
public void setTabla(String valor)
{
this.tabla = valor;
}
public String getTabla()
{
return
tabla;
}
public void setCampos(String
valor)
{
this.campos = valor;
}
public String getCampos()
{
return campos;
}
public void setWhereClause(String valor)
{
this.whereClause
= valor;
}
public String getWhereClause()
{
return whereClause;
}
public String[][] getDatos()
{
return getDatos(tabla, campos, whereClause);
}
public String[][] getDatos(String tabla, String campos,
String whereClause)
{
String matrix[][]
= null;
Statement stmt;
ResultSet
rs;
// contar num de
campos. Ejemplo : "nom, tel1, tel2, fax"
StringTokenizer st = new StringTokenizer(campos, ",");
int numColumnas = st.countTokens();
if (whereClause == null) whereClause = "";
if (!whereClause.equals("")) whereClause = "
WHERE " + whereClause;
query
= "SELECT " + campos
+
" FROM " + tabla
+
whereClause;
try
{
// contar filas
stmt = con.createStatement();
rs
= stmt.executeQuery("SELECT COUNT(*) "
+
"FROM " + tabla
+
whereClause);
rs.next();
int numFilas = rs.getInt(1);
// Ahora ya podemos reservar la memoria para
la matriz
// porque ya renemos las filas
y las columnas
matrix
= new String[numFilas][numColumnas];
stmt = con.createStatement();
rs
= stmt.executeQuery(query);
int
i = 0;
while (rs.next())
{
for (int j = 0; j < numColumnas;
j++)
{
matrix[i][j]
= rs.getString(j+1);
}
i++;
}
rs.close();
stmt.close();
}
catch (SQLException e)
{
e.printStackTrace();
}
return matrix;
}
public void setQuery(String valor)
{
this.query = valor;
}
public String getQuery()
{
return
query;
}
public int ejecutarSQL()
throws SQLException
{
return ejecutarSQL(query);
}
public int ejecutarSQL(String query)
throws SQLException
{
Statement stmt;
// SQL statement object
stmt
= con.createStatement();
return stmt.executeUpdate(query);
}
/**
* Banco de
pruebas
*/
public static void main(String args[])
throws Exception
{
try
{
BDAdaptorBean g = new BDAdaptorBean();
//
Comentadas la lineas para SQL Server (ó MSDE que viene en el Office 2000 como
personal SQL Server)
//
Establecimiento de sesión
g.setOrigenDatos("jdbc:odbc:Neptuno");
g.setUsername("Admin"); // g.setUsername("sa");
g.setPassword("");
g.conectar();
//
Ejecución de una instrucción SQL
g.setTabla("empleados");
// g.setTabla("employees");
g.setCampos("nombre,
apellidos"); // g.setCampos("firstname,
lastname");
g.setWhereClause("nombre
like 'N%'"); // g.setWhereClause("firstname
like 'N%'");
//
String resultados[][] = g.getDatos("Empleados", "Nombre, Apellidos",
"Nombre like '%'");
String resultados[][]
= g.getDatos();
for
(int i = 0; i < resultados.length; i++)
{
System.out.print(i + ": ");
for (int j = 0; j < resultados[i].length;
j++)
{
System.out.print(resultados[i][j]
+ " ");
}
System.out.println("");
}
System.out.println("------------Fin----------");
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
La adaptación de este ejemplo en un servlet sugiere un comentario. Los servlets ofrecen básicamente dos métodos: el método init() y el método service() (con las variantes doGet(), doPost(), etc.)
El método init() se ejecuta la primera vez que se llama al servlet. Es parecido al init() de los applets: Se ejecuta en la carga y el servlet queda persistente. Del mismo modo destroy() se ejecuta al descargarse.
Los servlets gozan de cierta fama de rápidos respecto a los CGI, incluso estando escritos en C. El truco consiste en obtener el objeto Connection en el init() y luego reutilizarlo para el resto de impactos. La obtención de un objeto Connection no es más que establecer un login en la base de datos. El proceso de login puede tardar unos segundos. Los servlets, al contrario que los CGI ofrecen la posibilidad de persistencia en memoria entre un impacto y otro, es decir mantener objetos ya instanciados permanentemente. Los CGI parten de cero cada vez y la persistencia se obtiene generalmente en base a ficheros.
En el siguiente ejemplo se muestra el HolaServlet modificado utilizando el BDAdaptorBean. En el método init() se establece la conexión. A partir de ahí el método service() utiliza una conexión ya creada. En una versión más depurada el método service() debería comprobar el estado de la conexión y reconstruirla en caso de haberse perdido:
//
// HolaServlet.java
//
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class HolaServlet extends HttpServlet
{
BDAdaptorBean g = new BDAdaptorBean();
public
void init(ServletConfig config) throws ServletException
{
super.init(config);
try
{
// Establecimiento
de sesión
g.setOrigenDatos("jdbc:odbc:Neptuno");
g.setUsername("admin");
g.setPassword("");
g.conectar();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public void service (HttpServletRequest
req, HttpServletResponse res) throws
ServletException,
IOException
{
PrintWriter p = res.getWriter();
res.setContentType("text/html");
p.println("<html>");
p.println("<body>");
p.println("<pre>"); // el tag
pre permite una salida de terminal ANSI en una página
try
{
//
Ejecución de una instrucción SQL
g.setTabla("Empleados");
g.setCampos("Nombre, Apellidos");
String
resultados[][] = g.getDatos();
for
(int i = 0; i < resultados.length; i++)
{
p.print(i + ": ");
for (int j = 0; j < resultados[i].length;
j++)
{
p.print(resultados[i][j]
+ " ");
}
p.println("");
}
p.println("------------Fin----------");
}
catch (Exception e)
{
e.printStackTrace();
}
p.println("</pre>");
p.println("</body>");
p.println("</html>");
p.close();
}
public
void destroy()
{
try
{
g.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
Para montar persistencia de objetos Java con mapeado a bases de datos SQL los PreparedStatement de JDBC pueden ser muy prácticos:
Cargar [loadRow()]
String
selectStatement =
"SELECT CODIGO, FECHA FROM TABLA WHERE CODIGO = ? ";
PreparedStatement prepStmt = con.prepareStatement(selectStatement);
String
cod = "1000";
prepStmt.setString(1, cod);
ResultSet rs = prepStmt.executeQuery();
Insertar [insertRow()]
String
insertStatement = "INSERT INTO TABLA VALUES (?, ?)";
PreparedStatement
prepStmt = con.prepareStatement(insertStatement);
String cod = "1000";
prepStmt.setString(1, cod);
prepStmt.setDate(2, new Date());
prepStmt.executeUpdate();
prepStmt.close();
Borrar [deleteRow()]
String
deleteStatement = "DELETE FROM TABLA WHERE ID = ? ";
PreparedStatement
prepStmt =
con.prepareStatement(deleteStatement);
String cod = "1000";
prepStmt.setString(1, cod);
prepStmt.executeUpdate();
prepStmt.close();
Modificar [storeRow()]
String
updateStatement = "UPDATE TABLA " +
" SET FECHA = ? "
+
" WHERE ID = ? ";
PreparedStatement prepStmt = con.prepareStatement(updateStatement);
String cod = "1000";
Date d = new Date();
prepStmt.setDate(1,
d);
prepStmt.setString(2, cod);
int rowCount = prepStmt.executeUpdate();
prepStmt.close();
Craig Larman plantea en su libro "UML y Patrones - Ed. Pearson" un modelo de persistencia de objetos. En base a ello y a las funciones anteriores se puede implementar una solución como la mostrada en EntityPersistUML.xls
7.- Ejemplo de scripting integrado en Java: Creación
de una clase intérprete de JavaScript con eval()
Puede
resultar interesante evaluar expresiones "al vuelo" en la parte servidora.
Esta idea es la base del concepto de "websheet" promulgado por Sun hace
ya tiempo que viene a ser una hoja de cálculo con interface web ejecutándose en
el servidor. El ejemplo utiliza el intérprete de JavaScript Rhino desarrollado
en Open Source por Mozilla disponible en http://www.mozilla.org/rhino
//
// JavaScript.java
//
import org.mozilla.javascript.*;
import
java.text.*;
public class JavaScript
{
private
Scriptable scope;
private Context cx;
public
JavaScript()
{
cx = Context.enter();
scope = cx.initStandardObjects(null);
}
public void finalize()
{
cx.exit();
}
public String eval(String expr)
throws JavaScriptException
{
Object
result = null;
result
= cx.evaluateString(scope, expr, "origen_script", 1, null);
return
result.toString();
}
public double
deval(String expr) throws JavaScriptException
{
return
Double.valueOf(eval(expr)).doubleValue();
}
public static void main(String[] args) throws Exception
{
JavaScript js = new JavaScript();
js.eval("x = 4");
js.eval("y
= 5");
js.eval("z = x + y");
System.out.println(js.eval("z"));
System.out.println("------");
double estropeadas = 23;
double repetidas = 73;
String
stRatio;
double ratio;
js.eval("ESTROPEADAS = " + estropeadas);
js.eval("REPETIDAS = " + repetidas);
js.eval("TOTAL
= ESTROPEADAS + REPETIDAS");
stRatio = js.eval("RATIO_ESTROPEADAS
= ESTROPEADAS / TOTAL");
System.out.println("RATIO_ESTROPEADAS:
" + stRatio);
ratio = js.deval("RATIO_ESTROPEADAS = ESTROPEADAS
/ TOTAL");
System.out.println("dbl_RATIO_ESTROPEADAS:
" + ratio);
System.out.println("---
Salida con formato ---");
DecimalFormat
df = new DecimalFormat("#,##0.##");
System.out.println(df.format(ratio
+ 5e-3));
System.out.println(df.format(ratio + 800000
+ 5e-3));
System.out.println(df.format(ratio + 800000/Math.PI
+ 5e-3));
}
}
8.- Desarrollo en arquitecturas multicapa (n-tier)
Para ilustrar el desarrollo n-capas nos basaremos en una colección de ejemplos que giran alrededor de un "mantenimiento de artículos" y que pueden obtenerse en los siguientes enlaces:
manart.zip,
manartjsp.zip,
manart.war
manartseguro.zip
manartj2ee.zip,
manartcmp.zip
cesta.zip,
cesta2.zip,
cesta.war
cestadb.war,
cestadb2.war,
cestadb3.war
Una mejora es añadir paginación en las tablas. Cómo implementarlo puede consultarse
en paginacion.txt
Para agilizar el desarrollo puede ser de gran ayuda contar con herramientas de
generación de código.
Un ejemplo de cómo aplicar seguridad a un mantenimiento generado puede descargarse
de clientes.zip
Arquitectura de
capas para aplicaciones Web. MVC modelo 1, modelo 2. Struts
En
los inicios los servlet eran una mejora de los CGI. Posteriormente los JSP, basados
en servlets permitían una mejor separación entre capa de presentación
y lógica de negocio. La inclusión de Java dentro de los JSP con
llamadas directas a éstos se conoce como MVC modelo 1. La inclusión
de un servlet redirector que recibe las peticiones HTTP y envía la llamada
a una clase para su procesamiento para ser reenviada a un JSP para mostrar (renderizar)
el resultado final se conoce como MVC model2. redirframe.war
es un ejemplo de ello. La explicación teórica puede consultarse
en http://www.informatizate.net/articulos/model_view_controller_mvc_20040324.html
No
obstante en el mercado existen implementaciones listas para usar de MVC model2
como struts (http://struts.apache.org).
Puede descargarse buenos tutoriales en castellano en http://www.programacion.net/tutorial/struts
y en http://www.adictosaltrabajo.com/tutoriales/tutoriales.php?pagina=strutsb