Archivo Mensual de Junio, 2006

Página 2 de 2

Servicios web (3): Desplegando un servicio WDSS

Como explicamos en la anterior entrada, el método para desplegar un servicio web usando JWS no sirve para un servicio real, o al menos no es lo más indicado. Por lo tanto voy a desplegar un servicio web usando el otro método que nos da Axis: usando un WSDD o descriptor de despliegue. Este fichero lo generaremos de manera automática, por lo que baste decir que es un fichero codificado en XML que contiene una descripción de lo que se despliga de un servicio web (tipo de despligue, clases, métodos, etcétera).

Escribiendo el servidor

Ya que vamos a ser formales, haremos que las clases del servicio formen parte de un paquete. Por lo demás, los cambios con la versión anterior son escasos: simplemente debemos hacer una interfaz para la clase que haga de servicio, ya que será dicha interfaz la que usemos para generar el fichero WSDL. Crearemos una carpeta con el nombre del paquete y escribiremos en ella la clase y su servicio. El código de la clase, HolaImpl.java apenas varía:


/*
Hola.java - La clase Java mas simple posible
*/
package hola;

public class HolaImpl {
  public String decirHola(String persona){
      return "Hola " + persona;
  }
}

Mientras que la interfaz, Hola.java es incluso más simple:


/*
* Hola.java - La interfaz para la clase Java mas simple posible
*/
package hola;

public interface Hola {
  public String decirHola(String persona);
}

Una vez hecho esto, basta crear “la estructura de directorios” que usaremos:


$ mkdir hola
$ mv Hola.java HolaImpl.java hola

Compilando y desplegando nuestro servicio web

En este caso, lo que debemos hacer es “ligeramente” más largo. Basicamente, se trata de los siguientes pasos:

  1. Compilar la clase del servicio y su interfaz. Sencillo paso, en resumidas cuentas es:
    $ javac hola/HolaImpl.java hola/Hola.java
  2. Generar el fichero WSDL. Para ello, usaremos Java2WSDL, que viene incluido en Axis. El mandato que yo he usado es el siguiente:
    $ java -cp .:$AXISCLASSPATH org.apache.axis.wsdl.Java2WSDL -o hola.wsdl
    -l"http://localhost:8080/axis/services/hola" -n urn:hola -p"hola" urn:hola
    hola.Hola

    Basicamente, le decimos que genere un WSDL con nombre hola.wsdl, cuyo espacio de nombres sea hola, y al que mapee el paquete hola. El último argumento especifica la clase Java a usar.
  3. Generar el resto de ficheros necesarios para el despliegue. Lo haremos a partir del WSDL generado, usando la clase WSDL2Java. El mandato que he usado para el ejemplo es:
    java -cp .:$AXISCLASSPATH org.apache.axis.wsdl.WSDL2Java -o . -d Session -s -p hola.ws hola.wsdl
    En resumen, le decimos que genere, a partir del directorio actual, creando una nueva clase servidora para cada sesión (también podemos usar application para todo el despliegue o request para cada invocación, que es la que viene por defector), generando también los ficheros para el despliegue del servicio, usando como paquete hola.ws, y a partir del fichero hola.wsdl, los ficheros necesarios para desplegar nuestro servicio. Los ficheros que se nos generan son los siguientes:

    • hola/ws/ – Directorio del paquete de los ficheros generados.
    • hola/ws/deploy.wsdd – Fichero para realizar el despliegue del servicio.
    • hola/ws/undeploy.wsdd – Fichero para realizar el repliegue del servicio.
    • hola/ws/Hola.java – Interfaz remota para nuestro servicio.
    • hola/ws/HolaService.java – Interfaz para el servicio web.
    • hola/ws/HolaServiceLocator.java – Implementación del interfaz anterior. Contiene métodos para localizar el servicio y cosas por el estilo.
    • hola/ws/HolaSoapBindingImpl.java – Envoltorio para el código del servicio. Proporciona acceso a los métodos del servicio web, pero están vacios. Hemos de editarlo y rellenarlo con código que acceda a nuestro código Java.
    • hola/ws/HolaSoapBindingStub.java – Stub del cliente. Encapsula su acceso al servicio.

    Por lo visto, además, se genera un skeleton para el servidor, pero en mi caso no sale. No sé porqué y de momento no me preocupo por ello (es posible que sea porque el servicio es muy sencillo, pero no estoy seguro. Cuando se la pegue, me preocuparé :-).

  4. Rellenar el fichero de envoltorio BindingImpl. Este paso no entiendo porqué no se hace solo, como los anteriores, ya que es totalmente mecánico (o al menos, se podría hacer una opción estándar, aunque luego lo puedas cambiar, con lo típico, que es lo que yo voy a hacer). Este fichero contiene una clase que proporciona una concha o envoltorio (wrapper es la típica palabra que se usa en inglés) para nuestra clase, sólo que esta es exportable como clase remota y tal. Simplemente debemos rellenar este envoltorio. En mi caso, el código ha quedado como sigue (en negrita los cambios que he realizado):
    
    package hola.ws;
    import hola.HolaImpl; // Añadido
    
    public class HolaSoapBindingImpl implements hola.ws.Hola{
    
        HolaImpl h = new HolaImpl(); // Añadido
    
        public java.lang.String decirHola(java.lang.String in0)
            throws java.rmi.RemoteException {
    
            return h.decirHola(in0); // Añadido
        }
    
    }
    

    Como veis, no hay nada especialmente “humano” en estos cambios, por eso no entiendo que no se hagan solos. Supongo que como la entrada de WSDL2Java es sólo el WSDL, y por lo tanto no se puede saber cómo se implementa el servicio, se tiene que dejar en blanco. Pero bueno, así no nos olvidamos de cómo programar, un poco aunque sea :-).

  5. Compilar las clases proporcionadas por WSDL2Java. Esto es, basicamente, un mandato similar al siguiente
    $ javac -classpath .:$AXISCLASSPATH hola/ws/*.java

    Acordaos siempre del classpath, o tendreis problemas al compilar.
  6. Poner nuestro código disponible para Axis. Una vez que tenemos todo listo, debemos indicarle a Axis cuál es la implementación de nuestro servicio web. Si no, aunque podremos desplegarlo, el cliente hará que Axis se la pegue al intentar ejecutar el servicio. La manera más limpia de hacerlo es empaquetar nuestro código en un jar y ponerlo en el lugar adecuado, algo como lo que sigue:
    $ jar cvf hola.jar hola/*.class hola/ws/*.class
    $ mv hola.jar $CATALINA_HOME/webapps/axis/WEB-INF/lib/

    Y aquí, lamentablemente, hay que reniciar el Tomcat para que se entere de este cambio. O por lo menos, yo lo he tenido que hacer en las pruebas que he hecho.
  7. Desplegar nuestro servicio web. Una vez que tengamos todo listo, sólo nos falta desplegar el servicio web. Esto es muy sencillo ya que Axis incorpora un servicio web para desplegar servicios web (mooola :-D). Así, con la siguiente orden lograremos el despliegue:
    java -cp $AXISCLASSPATH org.apache.axis.client.AdminClient hola/ws/deploy.wsdd

    Si todo va bien (crucemos los dedos) obtenemos una salida similar a esta:
    log4j:WARN No appenders could be found for logger (org.apache.axis.i18n.ProjectResourceBundle).
    log4j:WARN Please initialize the log4j system properly.
    Processing file hola/ws/deploy.wsdd
    Done processing

    Esperemos que en un futuro, los dichosos warnings desaparezan (cuando lo arregle, supongo). Si vamos a ver los servicios desplegados, veremos nuestro servicio ahí colgado. Para desplegarlo, tan fácil como:
    java -cp $AXISCLASSPATH org.apache.axis.client.AdminClient hola/ws/undeploy.wsdd

    Nada de reinicios de tomcat ni cosas por el estilo como en ocasiones pasadas, excepto el del apartado anterior. No está mal.

Escribiendo un cliente con stubs generados

. De acuerdo a la forma en que estamos haciendo nuestro servicio web, ya no tiene lógica usar interfaces de invocación dinámica para el cliente, sino que usaremos los stubs generados por WSDL2Java. De esta manera, el código correspondiente al endpoint, el servicio y la llamada están en el stub y el código del cliente queda mucho más limpio. Además, podremos obtener en el cliente un objeto remoto en vez de una llamada, lo cual será más cómodo en el caso de trabajar con varias invocaciones a port-types (operaciones) del servicio web (vamos, el caso normal). El código del cliente, en mi caso, queda como algo así:

/*
 * HolaCliente.java - Cliente con stubs generados para el servicio web
 *                    mas simple posible
 */

package hola;

import hola.ws.HolaService;
import hola.ws.HolaServiceLocator;
import hola.ws.Hola;

public class HolaCliente {
  public static void main(String [] args) throws Exception {

      // Solicitamos el servicio a su localizador
      HolaService servicio = new HolaServiceLocator();

      // Usamos el servicio para conseguir un stub
      Hola hola = servicio.gethola();

      // Hacemos las llamadas que queramos
      System.out.println(hola.decirHola(" cliente"));
  }
}

Como vemos, queda mucho más limpio que el anterior, y una vez que tenemos el objeto hola, podemos hacer todas las llamadas a sus operaciones del tirón, sin preparar cada llamada, como en el cliente anterior.

Compilando y ejecutando el cliente

Lo que queda es fácil. Compilar el cliente:
$ javac -classpath .:$AXISCLASSPATH hola/HolaCliente.java

Y lanzarlo a la batalla:
$ java -cp .:$AXISCLASSPATH hola.HolaCliente

Y si todo va bien, saldrá algo parecido a esto:
log4j:WARN No appenders could be found for logger (org.apache.axis.i18n.ProjectResourceBundle).
log4j:WARN Please initialize the log4j system properly.
Hola cliente

Si no va bien y os salen excepciones a porrillo, puede ser por lo que he comentado antes de reiniciar el Tomcat cuando moveis el código del servicio.

Y esto es todo por hoy. Si quereis el código fuente que yo he desarrollado, este es hola/Hola.java, hola/HolaImpl.java, hola/HolaCliente.java, y hola/ws/HolaSoapBindingImpl.java.

Y ahora como bonus track un resumencillo de todos los mandatos en orden:
$ javac hola/HolaImpl.java hola/Hola.java
$ java -cp .:$AXISCLASSPATH org.apache.axis.wsdl.Java2WSDL -o hola.wsdl -l"http://localhost:8080/axis/services/hola" -n urn:hola -p"hola" urn:hola hola.Hola
$ java -cp .:$AXISCLASSPATH org.apache.axis.wsdl.WSDL2Java -o .
-d Session -s -p hola.ws hola.wsdl
$ javac -classpath .:$AXISCLASSPATH hola/ws/*.java
$ jar cvf hola.jar hola/*.class hola/ws/*.class
$ mv hola.jar $CATALINA_HOME/webapps/axis/WEB-INF/lib/
$ java -cp $AXISCLASSPATH org.apache.axis.client.AdminClient hola/ws/deploy.wsdd
$ javac -classpath .:$AXISCLASSPATH hola/HolaCliente.java
$ java -cp $AXISCLASSPATH org.apache.axis.client.AdminClient hola/ws/undeploy.wsdd

Technorati Tags:

Servicios web (2): Desplegando un servicio JWS

Una vez que ya tenemos Tomcat y Axis instalados y funcionando, el siguiente paso es desplegar un servicio web. Existen dos maneras de hacerlo, de las cuales yo voy a abordar la primera en este post. También deberemos escribir un cliente. Para ello, existen tres posibilidades, yo sólo voy a tratar con dos de ellas.

La primera manera de despleguar un servicio web, JWS, es muy sencilla y sólo está indicada para hacer pruebas o para empezar. Consiste en esribir una clase en Java con una serie de métodos, de tal manera que esta se convierta por si sóla en un servicio web, sin tener que hacer nada más. La conversión, creo entender, se hace al vuelo, por eso no está recomendado usarlo: incrementaría mucho la carga del servidor y además no podemos modificar el fichero WSDL.

Escribiendo el servidor

En mi caso he escrito el programa más sencillo del mundo, el clasico Hola, aunque he quitado el Mundo porque me parece muy pretencioso ;-). El código fuente es como se ve a continuación:


/*
 Hola.java - El ejemplo Java mas simple
 */

public class Hola {
  public String decirHola(String persona) {
      return "Hola " + persona;
  }
}

Desplegando el servicio web

Para desplegar este servicio web, basta con copiar el fichero al directorio webapps de axis, pero cambiando la extensión a jws. Esto es:

cp Hola.java $CATALINA_HOME/webapps/axis/Hola.jws

Una vez hecho esto, podemos visitar nuestro servicio web. Evidentemente, no es una página web, pero Axis nos responde avisándonos de que es un servicio web, y nos permite ver el WSDL asociado, lo que para hacer pruebas o aprender es bastante útil.

Escribiendo un cliente

Existen varios métodos para escribir un cliente, como ya dije al principio. Voy a usar el que mejor corresponde al método para desplegar el servicio: el más sencillo. Éste método consiste en crear una DDI o interfaz de invocación dinámica. Su ventaja es la sencillez, pero tiene como desventaja el que no obtendremos una referencia a un objeto remoto, sino a un método, lo cual es menos cómodo.
Primero deberemos importar una serie de librerías de Axis y una de Java:

import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.apache.axis.encoding.XMLType;
import javax.xml.rpc.ParameterMode;

A continuación, declararemos una clase de las que se ejecutan, es decir, con un método estático main. Lo primero que hay que hacer es tener claro cuál es nuestro endpoint, que es la dirección del servicio web. En mi ejemplo:

public class HolaCliente {
   public static void main(String [] args) throws Exception {

       String endpoint = "http://localhost:8080/axis/Hola.jws";

A continuación, declararemos un servicio y una llamada para el mismo. A esta llamada le indicaremos cual es su endpoint, la operación a realizar y los parámetros de la misma, así como el tipo de retorno. Por último la invocaremos:


       Service  service = new Service();
       Call call = (Call)service.createCall();

       call.setTargetEndpointAddress(new java.net.URL(endpoint));
       call.setOperationName("decirHola");
       call.addParameter("persona", XMLType.XSD_STRING,
                         ParameterMode.IN);
       call.setReturnType(XMLType.XSD_STRING);

       String resultado = (String)call.invoke(new Object [] { " cliente"});

       System.out.println(resultado);
   }
}

Compilando y ejecutando el cliente

Para compilar nuestro cliente, debemos tener en cuenta el classpath del compilador de Java, ya que debe incluir el de Axis. Puedes hacerlo usando variables de entorno, aunque yo prefiero pasárselo como un argumento de la llamada al compilador, tal que así:

$ javac -classpath $AXISCLASSPATH HolaCliente.java

Si todo ha ido bien, ya podemos lanzar nuestro cliente. Hay que tener en cuenta ahora que en el classpath debe figurar el directorio donde está el resultado de la compilación anterior. En mi caso, hago:

$ java -cp .:$AXISCLASSPATH HolaCliente

Lo que hace que obtenga el siguiente resultado:
log4j:WARN No appenders could be found for logger (org.apache.axis.i18n.ProjectResourceBundle).
log4j:WARN Please initialize the log4j system properly.
Hola cliente

Que, a pesar de los warnings que sigo ignorando, da un resultado safistactorio :-). Los ficheros con el código fuente son Hola.java y HolaCliente.java

Precede: Servicios web (1): Instalando Tomcat y Axis
Continua: Servicios web (3): Desplegando un servicio WDSS

Technorati Tags:

Spammers más listos para mi Gmail

Que cabroncetes son los spammers, pero también que listos los jodidos. Ayer me sorpredieron 4 falsos negativos seguidos en mi correo de Gmail, es decir, me llegaron 4 correos de spam a la bandeja de entrada.. ¡En 5 minutos! Normalmente me llega uno a la semana, mientras que la carpeta de spam no para de crecer y crecer (llegarán cerca de 50 al día). ¿Qué es lo que pasó? ¿Cómo se colaron? Resulta que los tíos ha leido mi correo o el correo que me llega de alguna manera, y me envían asuntos que cuadran con cosas que yo tengo en mi bandeja de entrada. Hice una captura para que se entienda a lo que me refiero (clickad si quereis verla más grande).

Los dos nuevos correos que veis en la bandeja de entrada tienen entre corchetes [] cosas que están en mi bandeja de entrada en este momento (en la imagen sólo se ve una de ellas, pero la otra también está). Además, curiosamente, ambas son siglas en mayúsculas.

Hoy me ha vuelto a pasar dos veces. ¿Será un fallo de seguridad de Gmail y los tíos están leyendo mi bandeja de entrada (o más bien sus robots) en ese momento o simplemente habrán analizado el tráfico?
En fin, maldito spam…

Technorati Tags:

Bush padre e hijo se fueron a pescar

Y como no, se trajeron un bonito recuerdo fotográfico:

Esto me recuerda a cuando Fraga se iba de caza mientras el Prestige destrozaba la costa gallega.. sólo que esto es un montaje (lo de irse de pesca, no el huracán :-P), y aquello no lo fue (Spain is different)…

Via email (gracias Pablerolas) (imagen original)

Technorati Tags: