Acceso a bases de datos

De VirtUAM-wiki
Saltar a: navegación, buscar
40x40px Esta página está actualizada a la versión OpenSim 0.7.5

Introducción

En muchas ocasiones puede ser necesario y de gran utilidad el guardar información en una base de datos de cosas que suceden en el mundo virtual, como por ejemplo una puntuación, y de la misma manera puede ser interesante realizar una consulta de dicha información desde dentro del simulador.
Para que los scripts tengan acceso a una base de datos es necesario tener instalado un servidor web, ya que lo que hacen en realidad es enviar peticiones HTTP a páginas JSP (Java Server Pages) que se encargan de realizar las operaciones necesarias con la base de datos.

Configurando el servidor web

  1. Descargamos e instalamos el servidor web Apache Tomcat: En él se alojarán los archivos a través de los cuales se accederá a la base de datos y las librerías y configuraciones necesarias.
  2. Descargamos el driver JDBC necesario para conectar la base de datos MySQL con las páginas JSP. Para instalarlo hay que pegar el archivo de nombre mysql-connector-java-*.bin.jar a la carpeta /lib del servidor tomcat. Una vez hecho esto, reiniciamos el servicio.
  3. En el directorio /webapps es el lugar en el que se alojan los sitios web. Creamos uno nuevo con las siguientes características:
Proyecto web jsp.jpg
  • index.jsp: Archivo en el que haremos las operaciones necesarias con la base de datos.
  • config.properties: En este archivo guardaremos los parámetros de conexión con la base de datos de manera que sean de fácil modificación y acceso.
  • cis69mc.jar: Librería necesaria para enviar mensajes XML-RPC desde el exterior al mundo virtual.

Escritura

Imaginemos que tenemos una base de datos llamada actividades en la que tenemos una tabla para la actividad_1 con los campos nombre y puntos.
Queremos guardar para el usuario Alumno Uno una puntuación de 8.
En el mundo virtual, desde un script, mediante la función llHTTPRequest() enviaremos la petición al servidor pasándole los parámetros que queremos insertar. El segundo parámetro debe ser un [HTTP_METHOD,"GET"] para indicar que se utilizan parámetros GET.

  • Veamos cómo quedaría el script con nuestro ejemplo:

<source lang="lsl2"> string NOMBRE = "Alumno Uno"; integer PUNTOS = 8;

default {

   touch_start(integer num)
   {
       // Al tocar el objeto, se realiza la solicitud indicando la dirección del servidor, 
       // del sitio web y del archivo JSP. A continuación añadimos los parámetros que queremos 
       // guardar de la siguiente manera.
       llHTTPRequest("http://150.244.58.190/Directorio Web/index.jsp?"+
                           "nombre="+ llEscapeURL(NOMBRE) +
                           "&puntos="+ PUNTOS               
                           , [HTTP_METHOD,"GET"], "");
      // Es muy recomendable que el string de la URL sea antes pasado a llEscapeURL() 
      // para evitar errores de sintaxis.
   }

} </source>

  • Ejemplo de la estructura del archivo /WEB-INF/classes/config.properties.

<source lang="properties">

  1. Este es el archivo contiene los parámetros de acceso
  2. a la base de datos.
  3. Estructura 'variable=valor'

ip=localhost port=3306 db=actividades user=usuario password=pwd </source>

  • Ahora analicemos nuestro archivo JSP:

<source lang="java"> <%@ page import="java.util.*" %> <%@ page import="java.sql.*" %>

<%! String ipDB, portDB, nameDB, userDB, passDB; // Parámetros de conexión con la base de datos. String nombre, puntos; // Datos a insertar. ResourceBundle rb; // Este elemento se utiliza para leer pares de valores. Connection conexion; Statement st; ResultSet rs; %> <html> <head> <title> Escritura JSP-DB</title> </head> <body> <%

rb = ResourceBundle.getBundle("config"); // Aquí llamamos al nombre del archivo con extensión ".properties"

                                                // en el que están los parámetros de conexión con la base de datos.

// Leemos los datos necesarios para la conexión con la base de datos

ipDB = rb.getString("ip"); portDB = rb.getString("port"); nameDB = rb.getString("db"); userDB = rb.getString("user"); passDB = rb.getString("password");

try {

       Class.forName("com.mysql.jdbc.Driver").newInstance(); // Inicializamos el driver de conexión JAVA-MYSQL
       conexion = DriverManager.getConnection("jdbc:mysql://"+ipDB+":"+portDB+"/"+nameDB,userDB,passDB); // Creamos la conexión con los parámetros leídos.

if (!conexion.isClosed()) { out.println("Conectado."); // Solicitamos los datos que queremos insertar. Los que se envían desde el script. nombre = request.getParameter("nombre"); puntos = request.getParameter("puntos");

// Comprobamos que los parámetros no estén vacíos. if (nombre!= null && puntos!=null) { // Ejecutamos la sentencia para insertar en la base de datos. Statement st = conexion.createStatement(); String sentencia = "insert into actividad_1 values ('"+ nombre +"', '"+ puntos +"')"; st.executeUpdate(sentencia); out.println("Datos insertados."); // Cerramos Statement st.close(); } else { out.println("No se recibieron parámemtros."); }

// Cerramos Connection. conexion.close(); } else { out.println("Error de conexión."); }

   }
   catch (Exception e) {
       e.printStackTrace();
   }	

%> </body> </html> </source>

De esta manera, al pulsar sobre el objeto, se realizará la solicitud al archivo JSP con los valores que queremos insertar y éste se encargará de realizar las operaciones necesarias para que esa operación se lleve a cabo.

Lectura

Para poder leer de una base de datos, se debe enviar una petición a un servidor web que a su vez responda con XML-RPC, el cual lanzará un evento remote_data en el script.

  • Lo primero que se debe hacer es activar el servicio XML-RPC en el archivo Opensim.ini. La sección XML-RPC no viene, pero se puede copiar del fichero OpenSimDefaults.ini y basta con descomentar las líneas dónde se establecen el XmlRpcRouterModule y el XmlRpcPort. Si esto no se hace será imposible recibir información de la base de datos.

<source lang="ini"> [XMLRPC]

   ; ##
   ; ## Scripting XMLRPC mapper
   ; ##
   ; If enabled, this will post an event, "xmlrpc_uri(string)" to the
   ; script concurrently with the first remote_data event.
   ; This will contain the fully qualified URI an external site needs
   ; to use to send XMLRPC requests to that script
   XmlRpcRouterModule = "XmlRpcRouterModule"
   XmlRpcPort = 20800

</source> Los valores del puerto XML-RPC y de la dirección del servidor en el que está alojado el simulador hay que añadirlos al archivo .properties para poder acceder a ellos en cualquier momento desde el archivo JSP.
Al igual que con las regiones y el simulador, hay que usar distintos valores en XmlRpcPort para regiones alojadas en el mismo servidor, de esta manera evitaremos conflitos.
Ahora imaginemos que queremos hacer una consulta desde el mundo virtual de la puntuación obtenida por el Alumno Uno.

  • El script para este caso, será distinto al de escritura:

<source lang="lsl2"> default {

   string NOMBRE = "Alumno Uno";
   touch_start(integer num)
   {
       // Lo primero que hay que hacer es habilitar un canal para la 
       // conexión XML-RPC.
       llOpenRemoteDataChannel();
   }
   remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval)
   {
       if (type == REMOTE_DATA_CHANNEL)
       {
           // Cuando recibimos la confirmación de que se ha creado un canal
           // lo enviamos al archivo JSP, junto con el nombre de la persona
           // de la que queremos conocer la puntuación, para que sepa que es 
           // por dónde debe comunicarse.
           string canal = (string) channel;
           // Enviamos la solicitud al servidor, llamando al archivo JSP y pasando valores.
           llHTTPRequest("http://150.244.58.190/Directorio Web/index.jsp?"+
                         "nombre="+llEscapeURL(NOMBRE)+
                         "&canal="+canal
                         ,[HTTP_METHOD,"GET"], "");              
       }
       else if (type = REMOTE_DATA_REQUEST)
       {
           // Aquí es dónde se recibe la información enviada por el archivo JSP, a través
           // del protocolo XML-RPC.
           // En este caso en 'ival' recibimos la puntuación del alumno.
           llOwnerSay("Nombre: "+sval+ " Puntos: "+ (string) ival);
           // Enviamos confirmación de que hemos recibido los datos.
           llRemoteDataReply(channel, NULL_KEY, "Exito", 1221);
       }
       else
       {
           llOwnerSay("Error.");
       }
   }

} </source>

40px  Como se observa, este medio de comunicación únicamente puede recibir un entero(ival) y una cadena (sval)
  • Ahora veamos la estructura del archivo JSP a invocar.

<source lang="java"> <%@ page import = "java.util.*" %> <%@ page import = "java.io.*" %> <%@ page import = "java.sql.*" %> <%@ page import = "org.apache.xmlrpc.*" %> // Importamos la librería xmlrpc a usar.

<%! String ipDB, portDB, nameDB, userDB, passDB, ipSIMULADOR, xmlrpcSIMULADOR; String nombre, canal; int puntos; ResourceBundle rb; Connection conexion; %> <html> <head> <title> Conexión XML-RPC</title> </head> <body> <% rb = ResourceBundle.getBundle("config_db");

// Leemos los datos necesarios para la conexión con la base de datos

ipDB = rb.getString("ip"); portDB = rb.getString("port"); nameDB = rb.getString("db"); userDB = rb.getString("user"); passDB = rb.getString("password");

       ipSIMULADOR = rb.getString("ipSim");
       xmlrpcSIMULADOR= rb.getString("xmlrpcSim");

try {

       Class.forName("com.mysql.jdbc.Driver").newInstance();
       conexion = DriverManager.getConnection("jdbc:mysql://"+ipDB+":"+portDB+"/"+nameDB,userDB,passDB);

if (!conexion.isClosed()) { out.println("Conectado."); // Solicitamos los datos necesarios para lectura y envío. canal = request.getParameter("canal"); nombre = request.getParameter("nombre");

// Comprobamos que los parámetros no estén vacíos. if (canal!= null && nombre != null) { // Consulta. String consulta = "select puntos from actividad_1 where nombre = '"+nombre+"'"; Statement st = conexion.createStatement(); ResultSet rs = st.executeQuery(consulta);

// Leemos el dato que queremos. if (rs.first() == true) { puntos = rs.getInt("puntos"); } // Cerramos Statement st.close(); out.println("LEIDO: "+nombre+ " y puntos: "+puntos);

// Preparamos los datos a enviar.

                               // Los parámetros debe ser un vector que contenga un hashtable que, a su vez contenga los pares clave-valor siguientes:
                               // Channel: El canal que ha enviado el script.
                               // IntValue: Un valor entero.
                               // StringValue: Un valor de tipo string.
                               // Se deben añadir los tres pares, aunque se envíen datos nulos, si no, fallará silenciosamente.

Hashtable theData = new Hashtable(); theData.put("Channel",canal); // Canal de comunicación generado dentro del mundo virtual. theData.put("IntValue",puntos); // Valor leído de la base de datos. theData.put("StringValue",nombre); // En este caso, para que no sea vacío, enviamos el nombre, aunque ya lo conocemos.

Vector params = new Vector(); params.add(theData);

                               // Preparamos la conexión XMLRPC y ejecutamos el envío.

XmlRpcClient server = new XmlRpcClient("http://"+ipSIMULADOR+":"+xmlrpcSIMULADOR+"/cgi-bin/xmlrpc.cgi"); Object result = server.execute("llRemoteData", params); out.println("Enviado "+result); } else { out.println("No se recibieron parámemtros."); }

// Cerramos Connection. conexion.close(); } else { out.println("Error de conexión."); }

   }
   catch (Exception e) {
       e.printStackTrace();
   }

%> </body> </html> </source>

De esta forma, al pulsar el objeto, se abre un canal de comunicación, que lo enviamos junto la información necesaria para obtener los datos que queremos de la base de datos invocando al archivo JSP alojado en nuestro servidor web. Éste se encarga de realizar las operaciones necesarias con la base de datos y de devolver a través de dicho canal la información solicitada.