Anexo A Código del proyecto En este anexo mostraremos el código del servicio, tanto del extremo del servidor como del Hotspot y resaltaremos los aspectos más interesantes de cada uno. A.1 El Hotspot El Hotspot consta de 11 archivos. Exponemos a continuación la lista de ello junto a un breve resumen: - escaner.c: donde se encuentra la función main - procesador.c / procesador.h: contienen las funciones relacionadas con SOAP y el procesado de dispositivos - auxiliar.c / auxiliar.h: diversas funciones auxiliares - soapC.c / soapH.h / soapClient.c / soapStub.h: archivos generados automáticamente por la herramienta gSOAP. - stdsoap2.c / stdsoap2.h : librerías de gSOAP A continuación vamos a dedicar un apartado a cada uno de estos ficheros. A.1.1 Escaner.c /* * * Archivo: escaner.c * Proyecto: hotspot * Autor: Jorge Calvillo Arbizu * Fecha: Septiembre 2007 * */ #include "auxiliar.h" #include "procesador.h" int main() { inquiry_info *ii = NULL; // almacena la lista de dispositivos detectados durante el inquiry int max_rsp, num_rsp; // número de respuestas/dispositivos detectados int dev_id; // identificador del adaptador Bluetooth local int socket; // socket HCI int len, i; int iteracion, estado; struct lista_dispositivos **inicio = NULL; struct lista_dispositivos *aux = NULL; char MAC_dev[20]; char nombre_dev[248]; inicio = &aux; // dirección MAC del dispositivo detectado // nombre del dispositivo detectado // Obtenemos el identificador del adaptador local Bluetooth dev_id = hci_get_route(NULL); if(dev_id < 0) { printf("[!] Error: dispositivo Bluetooth no disponible\n"); exit(1); } // Abrimos un socket local HCI socket = hci_open_dev(dev_id); if(socket < 0) { printf("[!] Error: fallo al intentar abrir el socket HCI\n"); exit(1); } // Inicializamos variables len = 8; max_rsp = 255; int timeout = 20000; // el tiempo de inquiry por dispositivo será de 1.28x8=10.24 seg/disp // se podrán detectar a lo sumo 255 dispositivos // tiempo máximo de espera // Entramos en el bucle infinito del Hotspot for (iteracion=1; ; iteracion++) { // Creamos la lista de dispositivos detectados con hci_inquiry ii = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info)); printf("+ Escaner de dispositivos Bluetooth: iteración %d +\n", iteracion); printf("Detectando dispositivos en el alcance...\n\n"); /* 'hci_inquiry' lleva a cabo un descubrimiento de dispositivos Bluetooth y devuelve una lista de * dispositivos detectados en 'inquiry_info ii' para ser almacenados. * La bandera IREQ_CACHE_FLUSH permite que la caché se limpie antes de buscar nuevos * dispositivos. En otro caso, podrían aparecer dispositivos anteriormente detectados pero ahora * fuera de rango */ num_rsp = hci_inquiry(dev_id, len, max_rsp, NULL, &ii, IREQ_CACHE_FLUSH); if(num_rsp < 0) printf("[!] Error: fallo al intentar realizar hci_inquiry\n"); else { // Llamamos a la función que procesa los resultados procesa_dispositivos(ii, inicio, num_rsp); /* Para cada una de las respuestas nuevas obtenidas durante el inquiry obtenemos el nombre * del dispositivo e imprimimos aquellos que han entrado en el área de influencia en este ciclo */ for(i=0; i<num_rsp; i++) { ba2str(&(ii+i)->bdaddr, MAC_dev); // convertimos la dirección a char memset(nombre_dev, 0, sizeof(nombre_dev)); // Llamamos a la función que recupera el nombre dado por el dispositivo if(hci_read_remote_name(socket, &(ii+i)->bdaddr, sizeof(nombre_dev), nombre_dev, timeout) < 0) strcpy(nombre_dev, "[Desconocido]"); estado = recupera_estado (MAC_dev, inicio); if (estado == ESTADO_NUEVO) printf("Dispositivo nuevo encontrado:\n\tMAC: %s\t Nombre asociado: %s\n\n", MAC_dev, nombre_dev); } // Por último, ponemos todos los estados a 0 para el siguiente ciclo de inquiry inicializa_estados (inicio); } free(ii); } // Fuera del bucle infinito, cerramos el socket y terminamos close(socket); return(0); } A.1.2 Procesador.c Adjuntamos tanto el fichero de código como su cabecera en este apartado. /* * * Archivo: procesador.c * Proyecto: hotspot * Autor: Jorge Calvillo Arbizu * Fecha: Septiembre 2007 * */ #include "auxiliar.h" #include "procesador.h" #include "accesoSoapBinding.nsmap" /* Esta función es la base del servicio pues es la que, tras comprobar que un dispositivo es * nuevo, lo inserta en la lista y realiza la petición al servidor. Si recibe un mensaje de * éste, entonces lo devuelve al terminal del cliente */ void procesa_dispositivos (inquiry_info *ii, struct lista_dispositivos **inicio, int num_rsp) { int canal_obex; // número del canal para transmisiones char comando_obex[100]; // cadena que almacenará el comando de envío int i; int presente = 0; /* En este bucle, por cada dispositivo detectado en el actual ciclo de inquiry recorremos * la lista del anterior ciclo y vemos si acaba de entrar en la zona de cobertura del nodo * o ya estaba en él desde entonces */ for(i=0; i<num_rsp; i++) { presente = recorre_lista (&(ii+i)->bdaddr, inicio); /* Si el dispositivo no estaba en el área en el ciclo de inquiry anterior debemos crear un * nuevo nodo en la lista y procesar su petición al servidor */ if(!presente) { crea_e_inserta_nodo ((ii+i)->bdaddr, inicio); // Enviamos la solicitud al servidor printf ("Enviamos la solicitud al servidor sobre una dirección\n"); struct soap *soap = soap_new(); char **respuesta; char MACdev[20]; char *archivo; ba2str(&(ii+i)->bdaddr, MACdev); // Si la petición se realiza con éxito deberemos recoger el mensaje adjunto if (soap_call_ns1__accesoservidor(soap, NULL, NULL, MACdev, NUM_NODO, respuesta) == SOAP_OK) { // Copiamos el adjunto recibido en un archivo struct soap_multipart *attachment; // Debemos recorrer los distintos adjuntos for (attachment = soap->mime.list; attachment; attachment = attachment->next) { if (attachment == soap->mime.list) { // Saltamos el primer adjunto que siempre es el propio mensaje SOAP continue; } fprintf(stdout, "Recibido tamaño del archivo %d.\n", attachment->size); FILE *fp; // Ahora, en función del tipo del adjunto lo almacenaremos con una extensión u otra if(!strcmp((*attachment).type,"image/jpeg")) archivo = "Imagen.jpeg"; // imagen/jpeg else if(!strcmp((*attachment).type,"application/octet-stream")) // audio/mp3 archivo = "Audio.mp3"; else if(!strcmp((*attachment).type,"text/html")) archivo = "Mensaje.html"; // texto/html else if(!strcmp((*attachment).type,"text/plain")) archivo = "Mensaje.txt"; // texto/plano else if(!strcmp((*attachment).type,"video/x-msvideo")) // video/avi archivo = "Video.avi"; else // tipo desconocido archivo = "ArchivoTmp"; fp = fopen(archivo, "w"); int retval = fwrite(attachment->ptr, 1, attachment->size, fp); if (retval != attachment->size) fprintf(stdout, "No se pudo descargar el archivo\n"); // y lo enviamos al terminal canal_obex = get_obex_channel(MACdev); sprintf (comando_obex, "obexftp -b %s -B %d -U -p %s", MACdev, canal_obex, archivo); system (comando_obex); fclose(fp); remove(archivo); } } else // en este caso ha ocurrido un error con la petición SOAP soap_print_fault(soap, stderr); // mostramos el fallo SOAP en el stream stderr soap_destroy(soap); soap_end(soap); } } //Ahora borramos todos los nodos de dispositivos que ya no están en el área de influencia (estado=0) borra_dispositivos (inicio); } /* Función que obtiene el canal RFCOMM utilizado por el perfil OBEX Object Push en el dispositivo, * necesario para la transmisión del mensaje al dispositivo del cliente */ int get_obex_channel(char *bt_address) { bdaddr_t bdaddr; uint16_t class = 0x1105; sdp_list_t *attrid, *search, *seq, *next; uint32_t range = 0x0000ffff; char str[20]; sdp_session_t *sess; uint32_t channel = -1; uuid_t group; bdaddr_t interface; str2ba(bt_address, &bdaddr); sdp_uuid16_create(&group, class); bacpy(&interface, BDADDR_ANY); sess = sdp_connect(&interface, &bdaddr, SDP_RETRY_IF_BUSY); ba2str(&bdaddr, str); if (!sess) { return -1; } attrid = sdp_list_append(0, &range); search = sdp_list_append(0, &group); if (sdp_service_search_attr_req(sess, search, SDP_ATTR_REQ_RANGE, attrid, &seq)) { sdp_close(sess); return -1; } sdp_list_free(attrid, 0); sdp_list_free(search, 0); for (; seq; seq = next) { sdp_record_t *rec = (sdp_record_t *) seq->data; sdp_list_t *proto = 0; if (sdp_get_access_protos(rec, &proto) == 0) { sdp_list_t* ptr = proto; for(;ptr != NULL;ptr = ptr->next){ sdp_list_t *protDescSeq = (sdp_list_t *)ptr->data; for(;protDescSeq != NULL;protDescSeq = protDescSeq->next){ channel = get_channel_from_service_desc(protDescSeq->data, NULL); if(channel != -1) break; } } sdp_list_free(proto, 0); } next = seq->next; free(seq); sdp_record_free(rec); } sdp_close(sess); return channel; } // Función auxiliar de get_obex_channel, que devuelve el canal del servicio pedido en el terminal int get_channel_from_service_desc(sdp_data_t *value, void *user) { char str[MAX_LEN_PROTOCOL_UUID_STR]; char UUID_str[MAX_LEN_UUID_STR]; sdp_data_t *p = (sdp_data_t *)value; int i = 0, proto = 0; for (; p; p = p->next, i++) { switch (p->dtd) { case SDP_UUID16: case SDP_UUID32: case SDP_UUID128: sdp_uuid2strn(&p->val.uuid, UUID_str, MAX_LEN_UUID_STR); sdp_proto_uuid2strn(&p->val.uuid, str, sizeof(str)); proto = sdp_uuid_to_proto(&p->val.uuid); break; case SDP_UINT8: if (proto == RFCOMM_UUID){ return p->val.uint8; } break; case SDP_UINT16: case SDP_SEQ16: case SDP_SEQ8: default: break; } } return -1; } A continuación, el archivo de cabecera que completa al anterior, procesador.h. /* * * Archivo: procesador.h * Proyecto: hotspot * Autor: Jorge Calvillo Arbizu * Fecha: Septiembre 2007 * */ #ifndef PROCESADOR_H #define PROCESADOR_H /* A este nodo Bluetooth le adjudicamos el número 1, pero cada nodo debe tener * un identificador diferente para que los pueda diferenciar el servidor * con motivo de la distribución de mensajes */ #define NUM_NODO 1 /* Esta función es la base del servicio pues es la que, tras comprobar que un dispositivo es * nuevo, lo inserta en la lista y realiza la petición al servidor. Si recibe un mensaje de * éste, entonces lo devuelve al terminal del cliente */ void procesa_dispositivos (inquiry_info *ii, struct lista_dispositivos **inicio, int num_rsp); /* Función que obtiene el canal RFCOMM utilizado por el perfil OBEX Object Push en el dispositivo, * necesario para la transmisión del mensaje al dispositivo del cliente */ int get_obex_channel(char *bt_address); // Función auxiliar de get_obex_channel, que devuelve el canal del servicio pedido en el terminal int get_channel_from_service_desc(sdp_data_t *value, void *user); #endif A.1.3 Auxiliar.c En este apartado incluimos el fichero auxiliar.c y su cabecera auxiliar.h que describen funciones de manejo de listas y otras tareas menos importantes. /* * * Archivo: auxiliar.c * Proyecto: hotspot * Autor: Jorge Calvillo Arbizu * Fecha: Septiembre 2007 * */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "auxiliar.h" /* Esta función es invocada cuando encontramos un nuevo dispositivo en el área de * cobertura del Hotspot y se encarga de crear un nodo con la información del terminal * y enlazarlo en la lista de dispositivos descubiertos */ void crea_e_inserta_nodo (bdaddr_t direccion, struct lista_dispositivos **inicio) { struct lista_dispositivos *predecesor, *sucesor, *nuevo; // Creamos nodo y rellenamos sus campos, con estado=ESTADO_NUEVO por ser nuevo dispositivo nuevo = (struct lista_dispositivos *)malloc(sizeof(struct lista_dispositivos)); nuevo->bdaddr = direccion; nuevo->estado = ESTADO_NUEVO; nuevo->siguiente = NULL; // Enlazamos en la lista if(*inicio == NULL) *inicio = nuevo; else { predecesor = *inicio; sucesor = predecesor->siguiente; while(sucesor != NULL) { predecesor = sucesor; sucesor = sucesor->siguiente; } predecesor->siguiente = nuevo; } } /* La siguiente función recorre la lista de dispositivos que guardamos del anterior * ciclo de inquiry. Si encuentra el dispositivo que estamos buscando cambia * su estado a ESTADO_ANTIGUO */ int recorre_lista (bdaddr_t *pdireccion, struct lista_dispositivos **inicio) { int encontrado = 0; struct lista_dispositivos *actual; char MAC_dev[20]; char MAC_actual[20]; actual = *inicio; // Cambiamos el tipo de la dirección MAC del dispositivo a cadena de char ba2str(pdireccion, MAC_dev); while (!encontrado && actual !=NULL) { ba2str(&(actual)->bdaddr, MAC_actual); // cogemos la dirección MAC del nodo if(strcmp(MAC_dev, MAC_actual)) // y comparamos ambas direcciones actual = actual->siguiente; // Sí coinciden else { encontrado = 1; actual->estado = ESTADO_ANTIGUO; } } return (encontrado); } /* La función 'borra_dispositivos' elimina de la lista los nodos de dispositivos que * han dejado el área de influencia y que, por tanto, tienen el estado a 0 */ void borra_dispositivos (struct lista_dispositivos **inicio) { struct lista_dispositivos *anterior, *borrar, *posterior; posterior = *inicio; // Si la lista está vacía no hacemos nada if (posterior != NULL) { /* Caso particular: si el primer nodo de la lista se tiene que borrar (es un caso recursivo * porque tras borrar el primero el que se hace cabeza de la lista también podría tener * que ser borrado)*/ while (posterior != NULL && !(posterior->estado)) { borrar = posterior; posterior = posterior->siguiente; *inicio = posterior; free(borrar); } // Caso general anterior = posterior; while (posterior != NULL) { posterior = posterior->siguiente; while(posterior != NULL && !(posterior->estado)) { borrar = posterior; posterior = posterior->siguiente; anterior->siguiente = posterior; free(borrar); } if (posterior != NULL && posterior->estado != 0) anterior = anterior->siguiente; } } } /* Recuperamos el estado del dispositivo. Sólo hay dos opciones: ESTADO_ANTIGUO si es * un dispositivo que está en el área desde el anterior ciclo de inquiry, o * ESTADO_NUEVO si es nuevo */ int recupera_estado (char *MAC_dev, struct lista_dispositivos **inicio) { struct lista_dispositivos *paux; int estado = 0; char MAC_actual[20]; paux = *inicio; while (!estado) { ba2str(&(paux)->bdaddr, MAC_actual); if(strcmp(MAC_dev, MAC_actual)) paux = paux->siguiente; else estado = paux->estado; } return (estado); } /* La siguiente función se ejecuta tras todo el procesado de los nuevos dispositivos * y antes de pasar a un nuevo ciclo de inquiry. Inicializa todos los estados a cero, * para no confundirlos con los nuevos que aparezcan en el próximo ciclo */ void inicializa_estados (struct lista_dispositivos **inicio) { struct lista_dispositivos *inicializa; inicializa = *inicio; while (inicializa != NULL) { inicializa->estado = 0; inicializa = inicializa->siguiente; } } Añadimos el fichero de cabecera auxiliar.h. /* * * Archivo: auxiliar.H * Proyecto: hotspot * Autor: Jorge Calvillo Arbizu * Fecha: Septiembre 2007 * */ #ifndef AUXILIAR_H #define AUXILIAR_H #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <bluetooth/bluetooth.h> #include <bluetooth/hci.h> #include <bluetooth/hci_lib.h> #include <bluetooth/sdp.h> #include <bluetooth/sdp_lib.h> #include "soapH.h" #define ESTADO_NUEVO 2 #define ESTADO_ANTIGUO 1 /* Tipo abstracto de los nodos de la lista que contiene su dirección MAC, su estado * y un puntero al siguiente elemento de la lista */ struct lista_dispositivos { bdaddr_t bdaddr; int estado; struct lista_dispositivos *siguiente; }; /* Esta función es invocada cuando encontramos un nuevo dispositivo en el área de * cobertura del Hotspot y se encarga de crear un nodo con la información del terminal * y enlazarlo en la lista de dispositivos descubiertos */ void crea_e_inserta_nodo (bdaddr_t direccion, struct lista_dispositivos **inicio); /* La siguiente función recorre la lista de dispositivos que guardamos del anterior * ciclo de inquiry. Si encuentra el dispositivo que estamos buscando cambia * su estado a ESTADO_NUEVO */ int recorre_lista (bdaddr_t *pdireccion, struct lista_dispositivos **inicio); /* La función borra_dispositivos elimina de la lista los nodos de dispositivos que * han dejado el área de influencia y que, por tanto, tienen el estado a 0 */ void borra_dispositivos (struct lista_dispositivos **inicio); /* Recuperamos el estado del dispositivo. Sólo hay dos opciones: ESTADO_ANTIGUO si * es un dispositivo que está en el área desde el anterior ciclo de inquiry, * o ESTADO_NUEVO si es nuevo */ int recupera_estado (char *MAC_dev, struct lista_dispositivos **inicio); /* La siguiente función se ejecuta tras todo el procesado de los nuevos dispositivos * y antes de pasar a un nuevo ciclo de inquiry. Inicializa todos los estados a cero, * para no confundirlos con los nuevos que aparezcan en el próximo ciclo */ void inicializa_estados (struct lista_dispositivos **inicio); #endif A.1.4 SoapC.c / SoapH.h / SoapStub.h / SoapClient.c Este conjunto de ficheros de código son generados automáticamente por la herramienta gSOAP por el proceso que se indica en el Anexo B. Almacenan las funciones necesarias para llevar a cabo esa comunicación, además de los procesos de serialización y deserialización. Dependen de nuestro servicio concreto, por eso varían de uno a otro proyecto. Estos ficheros son muy extensos y por eso se omiten en este documento, volviendo a remitir al lector interesado a su proceso de generación en el Anexo B. A.1.5 Stdsoap2.c / Stdsoap2.h Estos dos ficheros son librerías de gSOAP y, como en el caso de los ficheros generados automáticamente del apartado anterior, se omite su contenido. Vienen incluidos en la distribución de gSOAP y contienen las estructuras y funciones generales de todas las comunicaciones SOAP. A.2 El Servidor El servidor consta de dos archivos principales: Acceso.java y AccesoInt.java, siendo el segundo la interfaz del primero. Además, contamos con otro conjunto de ficheros de código creados automáticamente por la herramienta Axis que posibilitan la exportación de este código a servicio web y que expondremos en el Anexo B. A continuación podemos ver el código de AccesoInt.java y Acceso.java. /* * * Archivo: AccesoInt.java * Proyecto: servidor * Autor: Jorge Calvillo Arbizu * Fecha: Septiembre 2007 * */ package servidor; // Interfaz de la clase Acceso public interface AccesoInt { /* Esta es la método que exportamos al servicio web y que realiza todo el trabajo. Recibe * una dirección MAC y el identificador del nodo bluetooth y hace las consultas pertinentes * a la base de datos y responde al nodo con un mensaje adjunto si existe */ String accesoservidor(String MACdev, int nodo_bluetooth); } /* * * Archivo: Acceso.java * Proyecto: servidor * Autor: Jorge Calvillo Arbizu * Fecha: Septiembre 2007 * */ package servidor; import java.sql.*; import javax.activation.DataHandler; import javax.activation.FileDataSource; import org.apache.axis.Message; import org.apache.axis.attachments.AttachmentPart; import org.apache.axis.MessageContext; public class Acceso implements AccesoInt { /* Esta es la método que exportamos al servicio web y que realiza todo el trabajo. Recibe * una dirección MAC y el identificador del nodo bluetooth y hace las consultas pertinentes * a la base de datos y responde al nodo con un mensaje adjunto si existe */ public String accesoservidor(String MACdev, int nodo_bluetooth){ System.out.println("\n---Comprobamos si el dispositivo detectado pertenece a un cliente---"); int id_cliente = 0; int i = 0; String archivo="No hay ningún archivo que cumpla con las condiciones"; try{ ResultSet rs; PreparedStatement pst; // aquí se almacenarán los resultados de las consultas // guardará las consultas a la base de datos // Registramos el controlador de JDBC para MySQL Class.forName("com.mysql.jdbc.Driver"); // La base de datos está en local y se llama servicio String url = "jdbc:mysql://localhost:3306/servicio"; // Conectamos con ella Connection con = DriverManager.getConnection(url, "mysql", "mysql"); // 1ª consulta: ¿la dirección MAC recibida pertenece a un usuario registrado? pst = con.prepareStatement ("SELECT * FROM clientes WHERE Terminal= ?"); pst.setString(1,MACdev); rs = pst.executeQuery(); // Comprobamos la respuesta de la base de datos while(rs.next()){ if(i == 0){ System.out.println("\n\tCliente encontrado: "); i++; } id_cliente = rs.getInt("ID_cliente"); // guardamos el identificador del cliente String str = rs.getString("Nombre_cliente"); // y su nombre System.out.println("\tID_cliente= " + id_cliente + "\tNombre_cliente = " + str); } // No hubo respuesta de la base de datos if (i == 0){ System.out.println("\n\tEl dispositivo detectado no pertenece a ningún cliente"); } //Si encontramos el cliente debemos devolver el mensaje que le corresponde if (id_cliente!=0){ System.out.println("\n---Buscamos mensajes relacionados con el nodo bluetooth y las áreas de preferencia del cliente---"); i = 0; try{ /* 2ª consulta: ¿existen mensajes que concuerden con las preferencias del cliente y que * puedan ser distribuidos por ese nodo bluetooth? */ pst = con.prepareStatement ("SELECT Contenido FROM mensajes WHERE (Nodo_bluetooth = ? && ((Area_ocio = (SELECT Area_ocio FROM clientes WHERE" + "ID_cliente = ?) && Area_ocio = \"1\") || (Area_viajes = (SELECT Area_viajes FROM clientes WHERE ID_cliente = ?) && Area_viajes = \"1\")" + " || (Area_moda = (SELECT Area_moda FROM clientes WHERE ID_cliente = ?) && Area_moda = \"1\") || (Area_cultura = (SELECT Area_cultura " + “FROM c clientes WHERE ID_cliente = ?) && Area_cultura = \"1\") || (Area_ofertas = (SELECT Area_ofertas FROM clientes WHERE ID_cliente = ?)" + “&& Area_ofertas = \"1\") || (Area_restaurantes = (SELECT Area_restaurantes FROM clientes WHERE ID_cliente = ?) && Area_restaurantes =" + "\"1\")) && ((texto_plano = (SELECT texto_plano FROM clientes WHERE ID_cliente = ?) && texto_plano = \"1\") || (texto_html = (SELECT " + " texto_html FROM clientes WHERE ID_cliente = ?) && texto_html = \"1\") || (imagen = (SELECT imagen FROM clientes WHERE ID_cliente = ?)" + “ && imagen = \"1\") || (audio = (SELECT audio FROM clientes WHERE ID_cliente = ?) && audio = \"1\") || (video = (SELECT video FROM clientes"+" WHERE ID_cliente = ?) && video = \"1\")))"); pst.setInt(1,nodo_bluetooth); pst.setInt(2, id_cliente); pst.setInt(3, id_cliente); pst.setInt(4, id_cliente); pst.setInt(5, id_cliente); pst.setInt(6, id_cliente); pst.setInt(7, id_cliente); pst.setInt(8, id_cliente); pst.setInt(9, id_cliente); pst.setInt(10, id_cliente); pst.setInt(11, id_cliente); pst.setInt(12, id_cliente); rs = pst.executeQuery(); System.out.println("\n\tMensaje a enviar al cliente:"); // Existe mensaje de respuesta if(rs.next()){ archivo = rs.getString("Contenido"); // guardamos la ruta del archivo a enviar // Adjuntamos el archivo al mensaje de respuesta SOAP MessageContext contexto = MessageContext.getCurrentContext(); Message respuesta = contexto.getResponseMessage(); respuesta.getAttachmentsImpl().setSendType (org.apache.axis.attachments.Attachments.SEND_TYPE_MIME); DataHandler dh = new DataHandler(new FileDataSource(archivo)); AttachmentPart att = (AttachmentPart)respuesta.createAttachmentPart(dh); respuesta.addAttachmentPart(att); } }catch( Exception e ) { e.printStackTrace(); } } // Cerramos la conexión con la base de datos con.close(); }catch( Exception e ) e.printStackTrace(); return (archivo); } }