Depende de si vamos a realizar un sistema de aplicaciones web o un servicio web, utilizaremos uno u otro.
Ya vimos las recomendaciones del Curso de Servicios Web de utilizar Glassfish para montar servicios web (WSDL) pero también se puede utilizar PHP y Apache, ya depende del número de usuarios y otros parámetros avanzados.
Este artículo se dirige a los que no saben tanto o para aquellos que deseen recordar o comentar los pros y contras de cada servidor, por qué utilizar un servidor con soporte Python (Django) o Rails (Ruby on Rails) en lugar de Apache con módulos para estas tecnologías, por qué no usar un servidor de Microsoft (ver llamadas al sistema y problemas de permisos en Windows),etc.
Empezaremos por una pequeña presentación y continuaremos con un par de vídeos relacionados.
(más…)
Para terminar con las Arquitecturas Web vamos a dejar que Pablo García Sánchez nos cuente qué otras hay además de las que hemos visto: Otras Arquitecturas y metodologías SOA
EJERCICIO SIMPLE
EJERCICIO AVANZADO
EJERCICIO BPEL AVANZADO: descargar
fin Del Curso amigos
« Volver al Curso de Arquitectura de Servicios Web con Java y PHP
Gracias a Pedro A. Castillo Valdivieso disponemos de este Curso de PHP:
Introducción a PHP – Programador PHP – UGR
Lo interesante, ahora que sabemos como programar servicios web complejos con Java y comunicarlos mediante SOAP es usar la clase que traen las últimas versiones de PHP (desde la 5.x), con un ejemplo sencillo, podemos probar a crear una lista de servicios como los que hay en http://www.service-repository.com/schema/namespaces …
El código sería tan sencillo como lo siguiente:
$wsdl = "http://www.currencyserver.de/webservice/currencyserverwebservice.asmx?WSDL"; echo "Servicios ofertados por <strong>$wsdl</strong>"; $client = new SoapClient($wsdl); $avail = $client->__getFunctions(); echo '<ul>'; foreach($avail as $func) { echo "<li>" . $func . "</li>"; } echo '</ul><hr />'; echo highlight_string( file_get_contents(__FILE__));
Que devuelve una lista de las operaciones del servicio especificadas por el WSDL.
Y para terminar ya sabéis que podéis crear vuestros propios servicios web con PHP, un ejemplo sencillo:
//Añadiendo función suma: $cliente = new SoapClient( null, array( "location"=>"http://localhost/simple/serversimple.php", "uri"=>"" ) ); $suma = $cliente->suma(2,3); print("La suma de 2 y 3 es :".$suma);
Código fuente de serversimple.php:
function suma($a,$b){ return intval($a)+intval($b); } $server = new SoapServer(null, array('uri'=>""));//Sin WSDL -> uri es obligatorio $server->addFunction("suma"); $server->handle();
Ver ejemplo funcionando: aquí
Recordar que hay que generar los ficheros WSDL con Katy u otra herramienta.
Una vez creado nuestro paquete con la aplicación Java para interactuar con los servicios web como hemos visto en la lección anterior, vamos a realizar las tareas que habíamos dicho paso por paso. Es decir, vamos a crear un servicio web con una operación para añadir un usuario con su nombre de usuario, password, un nombre de piloto y una nave con su cantidad de armamento, otro para obtener una lista de usuarios con pilotos y naves y otro para hacer un login con usuario y password, para ello:
CREAR DIRECTORIO DE XML:
CREAR SERVICIO WEB
CREAR OPERACIÓN PARA AÑADIR UN USUARIO CON SUS PILOTOS Y SUS NAVES ASOCIADAS
@WebMethod(operationName = "crearUsuarioConPilotosYnaves") public Long crearUsuarioConPilotosYnaves(@WebParam(name = "datos_usuario") Usuario datos_usuario, @WebParam(name = "datos_piloto") Piloto datos_piloto, @WebParam(name = "datos_nave") Viper datos_nave) { if (nexus.obtenerUsuario(datos_usuario.getUsuario())!=null ){ //El usuario ya existe, salir! return new Long(-1); } //Añadir Viper Viper v = null; try { //Buscar viper: v = nexus.obtenerViper(datos_nave.getNombre()); } catch (ViperNoEncontradoException ex){ //La nave no existe, la añadimos v = new Viper(datos_nave.getNombre()); v.setArmamento(datos_nave.getArmamento()); } finally { //En otro caso, la nave es la misma? if (!v.getId().equals(datos_nave.getId())){ //No es la misma, error return new Long(-1); } } //Añadir piloto: Piloto p = null; try { p = nexus.obtenerPiloto(datos_piloto.getId()); } catch (PilotoNoEncontradoException ex){ //NO Existe el piloto, crearlo p = new Piloto(datos_piloto.getNombre(), datos_piloto.getDestreza(), v.getId()); nexus.guardarPiloto(p); } finally { //El piloto existe, tiene la misma ID? if (!p.getId().equals(datos_piloto.getId())){ return new Long(-1); } } ArrayList<Long> lista_pilotos = new ArrayList<Long>(); lista_pilotos.add(p.getId()); //Añadir el usuario con los datos almacenados ya en Nexus: Usuario u = new Usuario( datos_usuario.getUsuario(), datos_usuario.getPassword(), lista_pilotos ); nexus.guardarUsuario(u); return u.getId(); }
Para probarla primero hacemos click con el segundo botón del ratón en el proyecto Excalibur > Deploy ,esto instalará la aplicación web con la nueva operación compilada, ahora vamos a la lista de servicios web y pulsamos en test Web Service, ya podemos añadir usuarios con un piloto y una nave
CREAR OPERACIÓN PARA HACER UN LOGIN CON USUARIO Y PASSWORD
@WebMethod(operationName = "hacerLogin") public String operation(@WebParam(name = "usuario") String nombre_usuario, @WebParam(name ="password") String contrasena) { //TODO write your implementation code here: Usuario u = nexus.obtenerUsuario(nombre_usuario); if (u==null) return "El usuario no existe"; else if (u.getPassword().equals(contrasena)){ return "Acceso concedido"; } else { return "Contraseña inválida"; } }
Recordar que para testear los servicios podemos hacerlo en SOAP UI:

CREAR OPERACIÓN PARA OBTENER UN LISTADO DE USUARIOS
public List<Usuario> listadoUsuarios() { List<Usuario> listado = null; for (Iterator<Usuario> iter = nexus.listarUsuarios(); iter.hasNext(); ){ listado.add(iter.next()); } return listado; }
Buenas prácticas de programación: Los servicios web son un tipo de comunicación, añadir lógica de programación resulta en algo no-generalizable y por lo tanto ,no reutilizable. Resumiento: hay que hacer una conversión de los datos de un modelo de datos en un modelo de datos de un servicio web,i.e.,las clases con su lógica de programación en Java a un modelo del servicio y luego el paso inverso. Entre medias están las páginas web u otros clientes que los utilizan con su propia lógica. Por ello se recomienda diseñar los servicios web lo último, cuando ya tenemos todo lo que necesitamos en cuanto a datos y lógica interna con ellos, separar cada fichero WSDL para cada servicio es recomendable por si necesitamos cambiar algo y nos cuesta menos trabajo.
CREAR CLIENTES PARA CONECTARSE A LOS SERVICIOS WEB
< Volver al curso de Arquitectura de Servicios Web con JAVA y PHP
Glassfish es el servidor de aplicaciones Web de Sun, como hay que crear un “dominio” dentro del servidor de aplicaciones y esto consume muchos recursos no se utiliza en máquinas convencionales ya que el coste de implementación de un servidor de dominios puede ser demasiado elevado por la carga que supone dicha tarea a pesar de que se puede implementar sobre Apache.
Si instalamos la última versión de Netbeans con Glassfish podemos pasar directamente a la sección de Crear el Modelo de datos al que acceder desde Servicios Web
Podemos configurar nuestro dominio tanto con la consola como dentro de NetBeans, usando la interfaz de usuario. Vamos a ver cómo se hace de las dos formas, primero con la consola:
Después de trabajar en lecciones anteriores con NetBeans, ya deberíamos conocer las ventajas de utilizar este IDE …
Ahora vamos a aprender cómo se crean servicios web que utilicen complejas estructuras de datos así como colecciones y anotaciones, reutilizables, desde distintos clientes que realicen las operaciones desde JAVA o PHP. También aprenderemos a definir un fichero WSDL para crear un servicio ,ambas cosas con Netbeans y usaremos JAX-WS para construir los clientes automáticamente, es decir, el cliente del servicio web (programa que lo usa desde la máquina de un usuario que se conecta a los servicios de un servidor) traduciendo los mensajes que se comunican por el protocolo SOAP.
Resumen de lo que vamos a hacer: crear un servicio web para registrar (en nuestro modelo de datos) usuarios, pilotos y naves en la base de datos de la clase que implementa el computador…recordar el proyecto BSGModelo…
Gracias a una clase llamada administracion que crea los usuariosse le asocia un piloto y a éste una nave (viper), todo esto queda almacenado en el modelo de datos del computador Nexus que se ejecuta sobre el servidor Excalibur (glassfish) y que lanza excepciones construidas por nosotros al nivel de aplicaciones y servicios web.
Abrir el proyecto de la lección 2: BSGModelo y haremos lo siguiente:
Para que probemos todos estos cambios, hemos hecho cambios a la función Main de la clase Prueba.java:
// Web 2.0: Arquitectura Orientada a Servicios en Java // Primer Modelo: BSGModelo con la clase Prueba // @author Juan Belón Pérez package es.ugr.battlegalactica; import es.ugr.battlegalactica.modelo.Elemento; import es.ugr.battlegalactica.modelo.Nexus; import es.ugr.battlegalactica.modelo.Piloto; import es.ugr.battlegalactica.modelo.Usuario; import es.ugr.battlegalactica.modelo.Viper; import es.ugr.battlegalactica.modelo.excepciones.NoHayArmamentoException; import es.ugr.battlegalactica.modelo.excepciones.PilotoNoEncontradoException; import es.ugr.battlegalactica.modelo.excepciones.ViperNoEncontradoException; import java.util.ArrayList; import java.util.Iterator; /** * Clase con los 3 primeros ejercicios propuestos * @author Juan Belón Pérez */ public class Prueba { private static ArrayList<Elemento> elementos; private static ArrayList<Piloto> pilotos; private static ArrayList<Viper> vipers; private static Nexus nexus; // Función principal. //Imprime por pantalla un mensaje fijo. // @param args la linea de argumentos del programa public static void main(String[] args) throws NoHayArmamentoException, PilotoNoEncontradoException, ViperNoEncontradoException { System.out.println("Prueba del modelo de datos BattleStarGallactica"); //crear dos elementos e imprimir sus identificadores en la consola. //Si todo ha ido bien se deberían escribir los identificadores 1 y 2 init(); System.out.println("Prueba de creación de elementos:"); for (Elemento e: elementos){ System.out.printf("%d,",e.getId()); } System.out.println("\nPrueba de creación de pilotos:\n"); for (Piloto p: pilotos){ System.out.printf("ID:%d,Nombre:%s,Destreza:%d\n",p.getId(),p.getNombre(), p.getDestreza()); } System.out.println("\nPrueba de creación de vipers:\n"); for (int i=0; i<4; i++){ //Disparar 4 veces con todos los vipers: for (Viper v: vipers){ System.out.printf("#%d# Disparando con el viper ID:%d,Nombre:%s," + "Armamento:%d\n",(i+1),v.getId(),v.getNombre(),v.getArmamento()); try { v.disparar(); } catch (NoHayArmamentoException e){ System.err.println("\n\tError al disparar:"+e.getMessage()+"\n"); } System.out.printf("\tEl nuevo armamento del viper con ID:%d y Nombre: %s"+ " ahora tiene %d unidad/es\n", v.getId(),v.getNombre(),v.getArmamento()); } } System.out.println("\nPrueba de creación de Nexus con Pilotos:\n"); Iterator<Piloto> iter_piloto = nexus.listarPilotos(); Piloto aux_piloto = null; while (iter_piloto.hasNext()){ aux_piloto = iter_piloto.next(); System.out.printf("Piloto %s, ID:%d\n", aux_piloto.getNombre(), aux_piloto.getId()); } System.out.println("\nPrueba de creación de Nexus con Vipers:\n"); Iterator<Viper> iter_viper = nexus.listarVipers(); Viper aux_viper = null; while (iter_viper.hasNext()){ aux_viper = iter_viper.next(); System.out.printf("Viper %s, ID:%d\n", aux_viper.getNombre(), aux_viper.getId()); } if (aux_piloto!=null){ System.out.println("\nPrueba de búsqueda (el último:" + aux_piloto.getId()+") de Pilotos en Nexus:\n" + nexus.obtenerPiloto(aux_piloto.getId()).getNombre()); } if (aux_viper!=null){ System.out.println("\nPrueba de búsqueda de Vipers (el último:"+ aux_viper.getId()+") en Nexus:\n" + nexus.obtenerViper(aux_viper.getId()).getNombre() ); } System.out.println("\nComprobar que se generan las excepciones "+ "buscando un Piloto falso:23\n"); try { aux_piloto = nexus.obtenerPiloto(new Long(23)); } catch (PilotoNoEncontradoException ex){ System.err.println("\n\t"+ex.getMessage()); } System.out.println("\nComprobar que se generan las excepciones "+ "buscando un Viper falso:23"); try { aux_viper = nexus.obtenerViper(new Long(23)); } catch (ViperNoEncontradoException ex){ System.err.println("\n\t"+ex.getMessage()); } try { Usuario usuario = nexus.obtenerUsuario("juaxix"); System.out.println("\nCreación de un usuario ("+ usuario.getUsuario() + " con piloto: " + (nexus.obtenerPiloto( usuario.getPilotos().get(0) ).getNombre()) + " y viper asociado a este piloto: "+ nexus.obtenerViper( nexus.obtenerPiloto( (nexus.obtenerUsuario(usuario.getId()).getPilotos().get(0)) ).getNaves().get(0) ).getNombre() ); System.out.flush(); } catch (PilotoNoEncontradoException ex){ System.err.println("\n\t"+ex.getMessage()); } catch (Exception ex){ System.err.println("\n\tNEXUS LOG:"+ex.getMessage()); } } // Inicializador de elementos, pilotos, vipers y Nexus (vipers, pilotos y usuarios) private static void init(){ Viper v_aux; elementos = new ArrayList<Elemento>(); pilotos = new ArrayList<Piloto>(); vipers = new ArrayList<Viper>(); nexus = new Nexus(); elementos.add(new Elemento()); elementos.add(new Elemento()); v_aux = new Viper("Trueno"); v_aux.setArmamento(4); vipers.add(v_aux); pilotos.add(new Piloto("Migue",120,v_aux.getId())); v_aux = new Viper("Rayo"); v_aux.setArmamento(4); vipers.add(v_aux); pilotos.add(new Piloto("Sara",110,v_aux.getId())); //INTRODUCIR DATOS EN NEXUS: v_aux = new Viper("Trueno"); v_aux.setArmamento(4); nexus.guardarViper(v_aux); Piloto juax = new Piloto("Juax",130,v_aux.getId()); nexus.guardarPiloto(juax); v_aux = new Viper("Fuego"); v_aux.setArmamento(5); nexus.guardarViper(v_aux); nexus.guardarPiloto(new Piloto("Jesús",130,v_aux.getId())); //Creación de un usuario en Nexus: ArrayList<Long> lista_pilotos_usuario = new ArrayList<Long>(); lista_pilotos_usuario .add(juax.getId()); Usuario usuario = new Usuario("juaxix" //nombre usuario/ , "juaxix" // password ,lista_pilotos_usuario // lista de ids de pilotos asociados ); nexus.guardarUsuario(usuario); } }
La idea del modelo de datos es parecida a un modelo de cajas:

La nueva línea que ha de mostrar la ejecución de esta clase (el proyecto BSGModelo) es:
“Creación de un usuario (juaxix con piloto: Juax y viper asociado a este piloto: Trueno”
< volver al Curso de Arquitectura de Servicios Web con JAVA + XML + PHP
Hablemos primero un poco acerca de XML Gracias a los protocolos para conectar XML como XML-RPC,SOAP o RSS, XML sobrevivió al desastre de convertirse en un lenguaje que no aportara nada nuevo a lo existente (HTML), y que combinado con WSDL, JAVA y PHP obtenemos las herramientas más utilizadas en la red hoy en día: servicios.
XML es robusto, podemos modificar el fichero fácilmente sin que pierda su significado dramáticamente; podemos construir una estructura para moldear la forma en que se comunican nuestros servicios con los clientes y aplicaciones con diversas reglas, me gustaría que mirárais la documentación sobre XML, XML-RPC,SOAP,RSS,SAX,DOM, JDOM y WSDL que he puesto aquí (de la UGR , y también en un comprido manuales_comunicacion.rar para tenerlo en local) ya que se explican conceptos fundamentales, como el standalone de xml (para decir si la estructura del árbol documento [DOM] se construye sólo con un fichero o por medio de varios, etc.), o lo nuevo de XML que son los namespace para que dentro del nombre de la etiqueta se pueda incrustar un diccionario: <dic:alumno>…</dic:alumno>, etc.; también, indicando donde está mediante <Nombre xmlns=”url_del_diccionario_nombre”>…
Sabiendo que DTD y un esquema XSD definen de la misma forma la estructura de un XML, estos últimos XSD son los más utilizados hoy en día, porque es realmente sencillo (al menos cuando estuve construyendo zenphp me costó mucho tener reglas DTD válidas para el generador de aplicaciones php a partir de un xml) hacer dichas reglas usando XML, ya que XSD se basa en un DTD ,son los diccionarios predefenidos que se van incrustando como las muñecas rusas.
El protocolo XML funciona normalmente a través del puerto HTTP (80) y dió lugar al XML-RPC, fué más famoso que SOAP porque éste había que introducirlo mediante un conjunto de reglas propias y era pesado de hacer, actualmente existen librerías que crean los envoltorios para enviar mensajes:
..embebiendo tipos en parámetros y estos en valores dando forma así a las peticiones y las respuestas de los servicios.
SOAP : es ligero, tiene pocas etiquetas y casi no pesan nada, es como la evolución de XML-RPC ,al fin y al cabo, es otro envoltorio para enviar mensajes por HTTP o SMTP , (Facebook lo usa)…
WSDL: además define reglas para un servicio web…mirar la documentación propuesta anteriormente para más información…
Para empezar, veremos como conectar Java con un WSDL y obtener resultados, siguiendo los pasos:
http://webservices.daehosting.com/services/
abrimos el servicio de fechas: http://webservices.daehosting.com/services/DatesService.wso y copiamos la dirección URL de la descripción de sus servicios ,que es el fichero WSDL [un XML] que necesitamos: http://webservices.daehosting.com/services/DatesService.wso?WSDL

Ahora que sabemos como comunicar servicios con esquemas XML veamos como utilizar Java para implementar una aplicación que haga lo que nuestro querido SOAP UI hace tan fácilmente en una serie de sencillos pasos:
http://www.ebi.ac.uk/Tools/webservices/wsdl/WSDbfetch.wsdl
si el tipo de Operación es SOAP en lugar de RPC, la acción SOAP la podemos leer en el editor SOAP UI en las propiedades de la Operación seleccionada
package EscribirXML.SOAP; import java.net.*; import java.io.*; /** * * @author */ public class ClienteSOAP { public final static String DEFAULT_SERVER = "http://www.ebi.ac.uk/Tools/webservices/wsdl/WSDbfetch.wsdl"; // = "http://www.schemaweb.info/webservices/soap/SchemaWebSoap.asmx?WSDL"; /* public final static String SOAP_ACTION = "http://www.schemaweb.info/webservices/methods/GetObjects";*/ public static void main(String[] args) { String server = DEFAULT_SERVER; try { URL u = new URL(server); URLConnection uc = u.openConnection(); HttpURLConnection connection = (HttpURLConnection) uc; connection.setDoOutput(true); connection.setDoInput(true); connection.setRequestMethod("POST"); //connection.setRequestProperty("SOAPAction", SOAP_ACTION); OutputStream out = connection.getOutputStream(); Writer wout = new OutputStreamWriter(out); wout.write( "<soapenv:Envelope xmlns:xsi=\""+ "http://www.w3.org/2001/XMLSchema-instance\" "+ "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "+ "xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" "+ "xmlns:wsd=\"http://wsdbfetch.ws.jdbfetch.ebi.ac.uk\">"+ "<soapenv:Header/>"+ "<soapenv:Body>"+ "<wsd:getSupportedDBs soapenv:encodingStyle=\""+ "http://schemas.xmlsoap.org/soap/encoding/\"/>"+ "</soapenv:Body>"+ "</soapenv:Envelope>"); wout.flush(); wout.close(); InputStream in = connection.getInputStream(); int c; System.err.println( "\n\nCONTESTACION\n\n"); while ((c = in.read()) != -1) System.out.write(c); in.close(); } catch (IOException e) { System.err.println("HA DADO UNA EXCEPCION"); System.err.println(e); } } // end main }

Por último vamos a probar un ejemplo del análisis sintáctico de un fichero XML a través del uso de un ParserDOM, abriendo el paquete DOM.Parsear y dentro el fichero “ParserDOM.java” sólo hemos de pasarle la ruta por línea de comandos del fichero y observar los resultados… (ver ejemplos con SAX del mismo proyecto de NetBeans proporcionado)…
El Modelo Objeto Documento de Java además de servir de analizador sintáctico con el método parse(), proporciona interfaces para manejar ficheros como árboles de datos, se obtiene con parsed.getDocument(), si no existen excepciones se trata, en otro caso es que el documento no ha pasado la validación, encontraremos información de prefijo, etc. dentro del árbol…