JSP (Java Server Page) - objetos accedidos a través de un sesión

 
Vista:

objetos accedidos a través de un sesión

Publicado por nepthis (37 intervenciones) el 11/01/2007 08:12:25
Hola de nuevo, me ha surgido un nuevo problema: vereis, en mi aplicación, cuando se accede a index.html se llama a un servlet, que crea una sesión y accede a la base de datos para recoger los tres ultimos avisos de la tabla de avisos y los guarda en el objeto session. El resto de páginas, si son accedidas en la misma sesión, hacen uso de este objeto y muestras estos avisos. El problema es que para poder hacer esto, tienes que haber entrado al sitio web por index.html. El día que se pueda acceder desde internet, si se accede a cualquier página sin haber pasado por index, que es la que activa la session, la página fallará. No se cómo solucionarlo, y me parece demasiado costoso y poco eficiente hacer un servlet por página. ¿Cómo puedo solucionarlo?

Muchas gracias
Valora esta pregunta
Me gusta: Está pregunta es útil y esta claraNo me gusta: Está pregunta no esta clara o no es útil
0
Responder

RE:objetos accedidos a través de un sesión

Publicado por Gonzalo (72 intervenciones) el 11/01/2007 08:52:27
Tienes varias opciones. Muchas en realidad. Cuál elegir depende un poco de qué tipo de fallo es el que ocurre, qué importancia tiene.

Básicamente hay dos casos:
- Que no deba poderse llegar a una página si no está cargado el objeto en cuestión.
- Que se pueda acceder y si el objeto no está, que no salga, pero que no falle.

Empecemos por el segundo caso, que es el más sencillo.
De hecho es tan simple como, antes de sacar los avisos, comprobar que estén.

Es decir, podríamos hacer algo como:
<%
TablaAvisos tabla = session.getAttribute("recientes");
if (tabla != null) {
%>
<!-- Sacamos la tabla de avisos -->
<%
} else {
%>
<!-- Sacamos un mensaje diciendo que la tabla no se ha cargado o una tabla vacía -->
<%
}
%>

Por supuesto esto se puede mejorar. Por ejemplo, si usas algo como Struts o similares, podrías hacer algo como:
<logic:present name="recientes">
<!-- sacamos la tabla -->
</logic:present>
<logic:notPresent name="recientes">
<!-- No sacamos la tabla -->
</logic:present>

El otro caso es un poco distinto. Lo que necesitas no es que salga la tabla en blanco, sino obligar a que pasen por index.html.

En este caso, lo que harías sería parecido, pero en lugar de sacar una tabla en blanco, redirigirías la página. Esto tiene que hacerse al principio de la página, antes de haber empezado a devolver nada.

En una primera aproximación podrías hacer algo como:
<% if (session.getAttribute("recientes") == null) response.sendRedirect("/index.html"); %>

Un poco más elegante sería crearte una etiqueta propia que compruebe todas las cosas que sean necesarias. Así, si más adelante necesitas comprobar más cosas, o simplemente tienes que hacer algún cambio, lo harías sólo en un sitio.

En tus páginas tendrías algo tan sencillo como:
<myapp:validasesion />

En el web.xml definirías la librería de etiquetas myapp y la localización de su TLD. En el TLD estaría definida la clase que implementa la etiqueta validasesion y dentro de esa clase el código sería la comprobación que teníamos antes incrustada.

Es un poco largo entrar aquí en detalles, pero si necesitas una explicación sobre librerías de etiquetas, tienes uno que está bien explicado aquí: http://www.programacion.net/java/articulo/customtags/
(Hay otros muchos, claro. Así que puedes buscar en un buscador algo como java etiquetas y ver qué encuentras)
En este caso, la etiqueta en cuestión es bastante sencilla (no necesita atributos, ni contenido), así que si no has hecho nada con etiquetas, es una buena forma de aprender a hacerlas.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

RE:objetos accedidos a través de un sesión

Publicado por nepthis (37 intervenciones) el 13/01/2007 18:01:38
Me has dado una idea excelente. He pensado en hacer un servlet que recibe las peticiones del usuario. Si no hay una sesión creada, la crea, accede a la base de datos, saca el vector con los ultimos avisos. Si ya está creada, según el parámetros con el que he invocado al servlet (en la url) redirige la salida a una página jsp u otra.
// SI LA SESIÓN NO HA SIDO CREADA

HttpSession session = request.getSession(true);
..........
// si ya está,
String x = request.getParameter("x");
y en función de x redirijo a una pagina u otra.
Esto funciona perfectamente si la sesión no ha sido creada.
Lo que no se, es cómo comprobar si ya hay una sesión.

Resumiento, mi idea es tener un servlet que recibe todas las peticiones. La primera vez creará una sesión y cargará los avisos. El resto de veces no, simplemente redirigirá la salida. Y esto para cada usuario.

Muchas gracias!
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

RE:objetos accedidos a través de un sesión

Publicado por nepthis (37 intervenciones) el 13/01/2007 20:27:51
Hola de nuevo!! He estado haciendo pruebas con respecto a lo que he expuesto anteriormente. Creo estar segura de lo que digo, pero me gustaría que alguien con más experiencia me lo confirmara. Al hacer

HttpSession session = request.getSession(true);

sólo se crea una sesión nueva para un usuario en caso de que ésta no exista ya. Si existe no la crea. ¿es así?

Por otro lado, cada usuario diferente que acceda al servlet recibe un identificador de sesión diferente, por lo que en el servidor habrá un objeto sesión por cada usuario, ¿es asi?

Lo que no entiendo muy bien, es cómo sabe el servidor que se trata de un nuevo usuario.

Muchas gracias.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

RE:objetos accedidos a través de un sesión

Publicado por Gonzalo (72 intervenciones) el 15/01/2007 09:48:50
Vamos por partes...

Hay dos formas de llamar a getSession:
- Si pides HttpSession session = request.getSession(true); o request.getSession() te devolverá una sesión nueva si no existe y la sesión existente si ya hay una (luego vemos cómo lo sabe).
- Si pides HttpSession session = request.getSession(false); te devolverá la sesión sólo si ya existe una. En caso contrario te devolverá nulo.

Una forma de usar esto es llamar con (true) o sin parámetros sólo en un sitio, en el sitio donde quieres crear la sesión. Es decir, en la página/acción de inicio, donde se carguen los recursos necesarios o se identifique o lo que haga falta. En el resto de sitios habrá que llamarlo con false y lo primero después de eso será comprobar que la sesión no es nula.

Otra forma es olvidarte un poco de si la sesión es nueva o no, y simplemente fijarte en lo que tiene. Llamarías siempre con request.getSession() y lo que haces es mirar si
session.getAttribute("recientes") != null

Ahora, cómo sabe si el usuario es nuevo o no...
Esto es algo que gestiona el servidor. El servidor envía una identificación única a cada usuario y normalmente lo hace basándose en su dirección IP. En principio no deberían preocuparte demasiado. Pero si te interesa saberlo, lo más habitual es que se base en la dirección de la máquina remota, que es más o menos única. Otra forma bastante habitual es:
Llega una petición, si no me manda el identificador de sesión (que es una pequeña cookie o un parámetro adicional que se inserta), quiere decir que es nuevo y le pongo uno. Si me lo manda, pues ya sé cuál es.
Como digo esto es transparente para tu aplicación y en principio no debería afectarte demasiado *cómo* se haga. Sólo que se haga bien.

Y sí, por cada usuario se crea una sesión y puedes crear objetos para cada usuario. Esto, normalmente, es lo que te interesa, al menos para determinados objetos. Si por ejemplo los "recientes" los cuentas desde la última vez que el usuario visitó la aplicación, entonces será propio de cada usuario.
Si por el contrario quieres crear un único objeto compartido por todos los usuarios, entonces en lugar de meterlo en la sesión lo tendrías a nivel de aplicación y podrías inicializarlo una sola vez, cuando se arranque la aplicación. Esto se aplicaría si por ejemplo los "recientes" los sacas con una fecha fija (p.ej. los 7 últimos días) que es igual para todos los usuarios.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

RE:objetos accedidos a través de un sesión

Publicado por nepthis (37 intervenciones) el 15/01/2007 19:42:15
Muchas gracias Gonzalo. Me he aclarado bastante y he simplificado la lógica de mi aplicacion :-)
Una cosita mas: es necesario redefinir en cada servlet el método init() y destroy() ? He leído que no, pero en ejemplos que he visto lo hacen siempre. Asi que no sé que hacer. Estos métodos ( init y destroy) se ocupa el servidor de invocarlos, yo no tengo que preocuparme de destruir un servlet no?

Gracias de nuevo!
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

RE:objetos accedidos a través de un sesión

Publicado por Gonzalo (72 intervenciones) el 17/01/2007 15:04:25
Los métodos init() y destroy() los llama el contenedor, exacto.

Los tendrás que implementar si necesitas hacer alguna operación al cargar o descargar el servlet, pero si no, no hace falta.

Si por ejemplo tienes una configuracion o unos datos que se cargan desde un fichero que sabes que no va a cambiar de una llamada a otra, lo suyo es leerlo una vez cuando se carga el servlet. Igualmente si necesitas algún recurso (una base de datos, un fichero de log...), lo tomas al arrancar el servlet y lo liberas al destruirlo.

Pero si no tienes nada similar, no hace falta redefinir esos métodos.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

RE:objetos accedidos a través de un sesión

Publicado por nepthis (1 intervención) el 17/01/2007 20:19:05
ahhhhhh, pues encontré un ejemplo que no hace eso. Hace cosas raras, que si te digo la verdad no entiendo muy bien como se ejecutan, pero el caso es que funciona. Te resumo brevemente el servlet que accede a la bd para coger información o para meterla. Crees que debería cambiarlo?

public class Avisos {

static String login = "";
static String password = "";
static String url = "";
Connection connection;
private boolean connectionFree = true;

private ArrayList avisos = new ArrayList();
private int rowsAffected = 0;

public Avisos () throws Exception
{
try
{
Class.forName("com.mysql.jdbc.Driver").newInstance();
this.connection = DriverManager.getConnection(url,login,password);
}
catch (Exception e)
{
throw new Exception ("No se pudo abrir la base de datos doctorado: " + e.getMessage ());
}
}

/*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: */

protected synchronized Connection getConnection () {
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 Avisos que esté esperando una conexión: */
notify ();

return this.connection;
}

protected synchronized void 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 Avisos 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.*/

public ArrayList getAvisos () {

try {
this.getConnection ();
PreparedStatement preparedStatement = this.connection.prepareStatement
("SELECT cod_aviso,cod_doctorado,fecha_alta,fecha_baja,titulo, contenido,visible FROM avisos");
ResultSet resultSet = preparedStatement.executeQuery ();

while (resultSet.next ()) {

……………………………………………………………..
}
preparedStatement.close ();
}
catch (SQLException e) {
return null;
}
this.releaseConnection ();

return avisos;
}


/* Metodo que INSERTA UN AVISO*/

public int insertarAviso (Aviso aviso) { /* recibe como parametro un objeto aviso */
try {
this.getConnection ();
PreparedStatement preparedStatement = this.connection.prepareStatement
("INSERT INTO avisos (cod_aviso,cod_doctorado, fecha_alta, fecha_baja, titulo, contenido, visible ) " +
" VALUES (?, ?, ?, ?, ?, ?, ?)");
...................................................
}
catch (SQLException e) {
this.releaseConnection ();
return 0;
}
return rowsAffected;
}
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

RE:objetos accedidos a través de un sesión

Publicado por Gonzalo (72 intervenciones) el 18/01/2007 16:28:43
Veamos...

Este que me has puesto es el tuyo o el que "hace cosas raras"?

Y cuando dices de cambiarlo, cambiar el que? Lo de usar el método init() y destroy()?

En este caso concreto, el método init() no te hace falta implementarlo porque haces la inicializacion en el constructor, pero si por ejemplo quisieras sacar la cadena "com.mysql.jdbc.Driver" a un fichero externo (si quisieras, que no digo que tengas que hacerlo, eh), entonces para cargarla necesitarías acceso a donde este ese fichero. Y eso lo sacas del ServlertConfig, que lo tienes disponible en el init() pero no necesariamente en el constructor.

En cuanto al destroy(), si que podrías tener una razón para implementarlo: liberar la conexión. Vale, quizá no es imprescindible, pero es más elegante cerrar la conexión a la base de datos. Evitas la posibilidad de problemas en el lado del servidor de bbdd. Así que podrías tener un método destroy en el que hicieras el close() de la connection.


No se si te referías a estos cambios o a alguna otra cosa. Si acaso pregunta.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

RE:objetos accedidos a través de un sesión

Publicado por nepthis (37 intervenciones) el 18/01/2007 19:23:55
Ante todo, y una vez más, MUCHAS GRACIAS!!

A ver, el código que te he mostrado es lo que ahora mismo estoy utilizando para acceder a la base de datos, que no tiene init() ni destroy(), por eso preguntaba que si sería mejor cambiarlo, porque la verdad, yo sé que funciona, que saca e inserta cosas en la base de datos, pero no sabía si era eficiente o no, o si daría problemas. Por tu respuesta veo que lo puedo dejar así. Referente al destroy, en el servlet que utilizo hay una función, que se me olvido pegar, que cierra la conexión, asi que supongo que hace lo mismo que haría el destroy(). Lo que no sé es quién llama a este método close(), debería hacerlo yo desde las funciones de insertarAviso,..., igual que llamo a "releaseConnection y getConnection"? o igual que desde otro servlet hago avisos.insertarAviso(..) hacer avisos.close??

public void close () {
try {
this.connection.close ();
}
catch (SQLException e) {
System.out.println (e.getMessage ());
}
}

Por cierto, acostumbrada a programar sacando posible fallos por pantalla, no consigo poner mensajes que me saquen errores en mi aplicación. Por ejemplo, si no estoy segura de que la ejecución del programa pase por una determinada parte de código o no, pongo un mensaje que me indique que pasa, pero no consigo ver ese mensaje. Supongo que cuando trabajas con servlet no es tan sencillo. ¿Cómo se puede hacer esto? Así sería mucho más facil saber , por ejemplo, porqué al invocar un servlet me muestra una página en blanco. Es decir, cuando hay problema de que no encuentra la ruta del forward o algo así, si salen los errores, pero si hay problemas al acceder a la base de datos, muestra la página en blando, pero no sé cuál ha sido el error.

Muchas gracias.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

RE:objetos accedidos a través de un sesión

Publicado por nepthis (37 intervenciones) el 18/01/2007 19:30:44
puffff, después de tanto texto, me he dado cuenta, que el problema que te he planteado antes del método close, de quién lo llama, sería tan fálcil como hacer

public void destroy () {
try {
this.connection.close ();
}
catch (SQLException e) {
System.out.println (e.getMessage ());
}
}

Como destroy() es invocado por el contenedor, le ha cambiado el nombre.
¿esta bien?
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

RE:objetos accedidos a través de un sesión

Publicado por Gonzalo (72 intervenciones) el 19/01/2007 09:30:06
Exacto! Esa es la idea. De hecho es que es lo que hay que hacer. La conexión la cierras cuando se vaya a descargar el servlet. Así que justo en el destroy es donde se debe hacer.

En cuanto a lo que comentas en el otro mensaje sobre sacar trazas... Bueno, teóricamente si haces un System.out.print() (o println) eso debería salir o bien por la consola del propio servidor, o bien por el fichero de log estándar del servidor. Si estás usando Tomcat es el fichero stdout.txt o stdout.log que se encuentra en el directorio logs de la instalación de Tomcat.

Por supuesto no es la mejor forma de seguir el desarrollo de tu servlet, porque ahí van un montón de cosas del propio servidor. Lo mejor es utilizar alguna librería, por ejemplo Log4J, o como mínimo hacerte una clase que escriba en un fichero tuyo propio y usar esa clase en tu servlet. Te recomiendo la opción de Log4J.

Aunque... si lo que quieres es, más que sacar trazas, poder debuguear la ejecución del servlet, puedes arrancar el servidor en modo debug y conectar con el desde Eclipse o el IDE que uses. O puedes incluso arrancarlo desde dentro de Eclipse o de NetBeans. Esto ya es un poco largo de explicar aquí, si acaso busca en algún buscador, que hay muchos sitios donde lo explican (si recuerdo alguno lo pondré, pero ahora mismo estoy de viaje y no tengo aquí mis enlaces).

Un saludo
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

RE:objetos accedidos a través de un sesión

Publicado por nepthis (37 intervenciones) el 20/01/2007 17:40:34
Me ha surgido una duda: Avisos.java cuyo código te he mostrado anteriormente, no es un servlet, es decir, no lo he metido en el web.xml, es un clase con la que accedo a la bd.Entonces, éste método que cierra la conexción con la bd, y que tiene que pertenecer a esta clase, aunque lo llame destroy (), como no es un servlet, ya no es invocado por el contenedor no?donde lo llamo entonces?

public void destroy () {
try {
this.connection.close ();
}
catch (SQLException e) {
System.out.println (e.getMessage ());
}
}

gracias.!!
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

RE:objetos accedidos a través de un sesión

Publicado por Gonzalo (72 intervenciones) el 20/01/2007 18:50:39
Bueno, esta es fácil.

Lo q
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

RE:objetos accedidos a través de un sesión

Publicado por Gonzalo (72 intervenciones) el 20/01/2007 18:50:39
Bueno, esta es fácil.

Lo q
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

RE:objetos accedidos a través de un sesión

Publicado por Gonzalo (72 intervenciones) el 20/01/2007 18:52:29
Lo que tienes que hacer es en Avisos tener tu método que cierre la conexión y que se llame como quieras (yo lo llamaria close). Y luego en tu servlet, tener un destroy que lo que haga sea llamar a .close() de tu objeto avisos.

No?
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar