Posted by: Camilo Torres | December 13, 2010

Pasar Datos del Header SOAP al Service Class de Axis2.

0. Introducción
En este artículo se explica como pasar datos que vienen en el HEADER de un mensaje SOAP hacia la clase que implementa el servicio con Axis2.

El problema es que se quieren enviar algunos datos en el encabezado SOAP, y esos datos podrían ser usados por la clase que implementa el servicio (en Axis2 esta clase se llama Service Class). Estos son los datos que se quieren enviar en el mensaje SOAP:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://www.example.org/ws/">
   <soapenv:Header>
     <a:Channel xmlns:a="asdf">Canal</a:Channel>
     <a:Username xmlns:a="asdf">username</a:Username>
     <a:messageID xmlns:a="asdf">mesageid</a:messageID>
   </soapenv:Header>
   <soapenv:Body>
      <ws:CatalogLookup>
         <CategoryCode>123</CategoryCode>
         <SearchTerm></SearchTerm>
         <MaximunPrizePoints>54</MaximunPrizePoints>
         <Channel>Canal</Channel>
         <Status>Active</Status>
      </ws:CatalogLookup>
   </soapenv:Body>
</soapenv:Envelope>

Básicamente son: Channel, Username y messageID. Ahora, esos datos deben ser pasados a la clase que implementa el servicio. En nuestro caso son varios los servicios que hay que hacer que deben obtener este encabezado, entonces estamos usando Eclipse 3.5 con los plugins de desarrollo web, primero generamos el WSDL con la herramienta de Eclipse.

1. Crear el servicio usando Axis2 con Eclipse.
Este excelente artículo Creando un servicio web a partir de su interfaz WSDL por Javier Cámara (http://www.adictosaltrabajo.com/tutoriales/tutoriales.php?pagina=WSDL2Java) tiene todas las instrucciones de como hacerlo.

2. Crear un cliente de pruebas.
Aquí hay un tutorial muy bueno Desarrollar un cliente webservice desde WSDL en Axis2 por Julio César Pérez Arques (http://jcesarperez.blogsome.com/2007/08/26/desarrollar-un-cliente-webservice-desde-wsdl-en-axis2/) que explica como crear el cliente. En nuestro caso solo nos interesan los clientes síncronos.

Luego de crear el cliente nos genera una clase Stub. Creamos una clase que contenga un método main() de Java y allí metemos nuestra prueba, incluyendo los encabezados:

package cliente;

import java.rmi.RemoteException;
import javax.xml.namespace.QName;
import org.apache.axis2.client.ServiceClient;
import cliente.WsStub.CatalogLookupResponse;
import cliente.WsStub.ItemType;

public class Prueba {
	public static void main(String[] args) throws RemoteException {
		// Se le indica el url donde se desplegó el servicio web
		WsStub ws = new WsStub("http://localhost:8080/PruebaWsdl4/services/ws?wsdl");
		
		// aquí pone los encabezados.
		ServiceClient sc = ws._getServiceClient();
		sc.addStringHeader(new QName("http://digitel.com.ve/", "Channel"), "mega");
		sc.addStringHeader(new QName("http://digitel.com.ve/", "Username"), "camilo");
		sc.addStringHeader(new QName("http://digitel.com.ve/", "messageID"), "msg23443");
		
		// aquí pone unos valores en el cuerpo del mensaje (los parámetros)
		WsStub.CatalogLookup cl = new WsStub.CatalogLookup();
		cl.setChannel("as");
		cl.setStatus(WsStub.Status_type1.Active);
		
		// hace la llamada al WS
		CatalogLookupResponse r = ws.CatalogLookup(cl);
		
		// nuestro WS retorna un arreglo de objetos, pero obtenemos
		// el primero y lo mostramos
		ItemType i = r.localItem[0];
		System.out.println(i.localChannel);
		System.out.println(i.localDisabled.getValue());
	}

}

Observe que poner los encabezados en el cliente es muy fácil, no así en el servidor. Hay otras formas de agregar encabezados al cliente como puedes ver en el artículo: Working with Custom SOAP Headers por Deepal Jayasingha (http://wso2.org/library/3156)

3. Crear el handler de Axis2 que leerá los encabezados en el Servicio.
Hay que crear un handler de Axis2 para poder leer los encabezados del servicio y pasarlos a la clase que implementa el servicio. En nuestro caso estamos implementando los servicios con el Skeleton que genera Axis2 mediante el plugin de Eclipse. Entonces creamos una clase dentro del proyecto web del servicio así:

package ve.com.ws.handler;

import java.util.Iterator;

import org.apache.axiom.om.OMElement;
import org.apache.axis2.AxisFault;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.handlers.AbstractHandler;
import org.apache.log4j.Logger;

public class HeaderHandler extends AbstractHandler {
	public static final Logger log = Logger.getLogger(HeaderHandler.class);

	@Override
	public InvocationResponse invoke(MessageContext mc) throws AxisFault {
		String headerName = null;
		String headerValue = null;
		
		Iterator it = mc.getEnvelope().getHeader().getChildElements();
		while (it.hasNext()) {
			OMElement o = it.next();
			headerName = o.getQName().getLocalPart();
			headerValue = o.getText();
			if (log.isDebugEnabled())
				log.debug("SOAP Message Header: " + headerName + "=" + headerValue);
			mc.setProperty(headerName, headerValue);
		}
		return InvocationResponse.CONTINUE;
	}

}

Observe que los valores se guardan en el MessageContext de Axis2. Luego la clase del servicio los tomará de allí.

4. Agregar el handler al axis2.xml para que sea invocado.
Para que el handler pueda correr hay que agregarlo al archivo WebContent/WEB-INF/conf/axis2.xml dentro del proyecto web. Busque la sección phaseOrder type=”InFlow” y agregue este trozo de XML:

        <phase name="HeaderPhase">
            <handler name="HeaderHandler"
                     class="ve.com.ws.handler.HeaderHandler">
                <order phase="HeaderPhase"/>
            </handler>
        </phase>

Para que quede de la siguiente forma:

    <phaseOrder type="InFlow">
        <!--  System predefined phases       -->
        <phase name="Transport">
            <handler name="RequestURIBasedDispatcher"
                     class="org.apache.axis2.dispatchers.RequestURIBasedDispatcher">
                <order phase="Transport"/>
            </handler>
            <handler name="SOAPActionBasedDispatcher"
                     class="org.apache.axis2.dispatchers.SOAPActionBasedDispatcher">
                <order phase="Transport"/>
            </handler>
        </phase>
        <phase name="Addressing">
             <handler name="AddressingBasedDispatcher"
                     class="org.apache.axis2.dispatchers.AddressingBasedDispatcher">
                 <order phase="Addressing"/>
            </handler>
        </phase>
        <phase name="Security"/>
        <phase name="PreDispatch"/>
        <phase name="Dispatch" class="org.apache.axis2.engine.DispatchPhase">
            <handler name="RequestURIBasedDispatcher"
                     class="org.apache.axis2.dispatchers.RequestURIBasedDispatcher"/>
            <handler name="SOAPActionBasedDispatcher"
                     class="org.apache.axis2.dispatchers.SOAPActionBasedDispatcher"/>
            <handler name="RequestURIOperationDispatcher"
                     class="org.apache.axis2.dispatchers.RequestURIOperationDispatcher"/>
            <handler name="SOAPMessageBodyBasedDispatcher"
                     class="org.apache.axis2.dispatchers.SOAPMessageBodyBasedDispatcher"/>
            <handler name="HTTPLocationBasedDispatcher"
                     class="org.apache.axis2.dispatchers.HTTPLocationBasedDispatcher"/>
            <handler name="GenericProviderDispatcher"
                     class="org.apache.axis2.jaxws.dispatchers.GenericProviderDispatcher"/>
            <handler name="MustUnderstandValidationDispatcher"
                     class="org.apache.axis2.jaxws.dispatchers.MustUnderstandValidationDispatcher"/>
        </phase>
        <phase name="RMPhase"/>
        <!--  System predefined phases       -->
        <!--   After Postdispatch phase module author or service author can add any phase he want      -->
        <phase name="OperationInPhase">
            <handler name="MustUnderstandChecker"
                     class="org.apache.axis2.jaxws.dispatchers.MustUnderstandChecker">
                <order phase="OperationInPhase"/>
            </handler>
        </phase>
        <phase name="soapmonitorPhase"/>
        <phase name="HeaderPhase">
            <handler name="HeaderHandler"
                     class="ve.com.ws.handler.HeaderHandler">
                <order phase="HeaderPhase"/>
            </handler>
        </phase>
    </phaseOrder>

Este artículo Handler and Phase in Apache Axis2 por Deepal Jayasinghe (http://www.packtpub.com/article/handler-and-phase-in-apache-axis) explica como es los de las fases (phase) y handlers de Axis2.

5. Modificar el esqueleto del servicio para que lea los encabezados.
Finalmente podemos leer los encabezados en el servicio. Edite la clase Skeleton que generó Eclipse para obtener los encabezados desde el MessageContext:

package org.example.www.ws;

import org.apache.axis2.context.MessageContext;

public class WsSkeleton {
	public org.example.www.ws.CatalogLookupResponse CatalogLookup(
			org.example.www.ws.CatalogLookup cl) {
		// lee los encabezados
		MessageContext mc = MessageContext.getCurrentMessageContext();
		String channel = (String) mc.getProperty("Channel");
		String username = (String) mc.getProperty("Username");
		String messageId = (String) mc.getProperty("messageID");
		System.out.println("Canal del encabezado: " + channel);
		System.out.println("Username del encabezado: " + username);
		System.out.println("messageId del encabezado: " + messageId);
		
		// imprime los valores que normalmente vienen en el cuerpo
		// es decir los parámetros normales
		System.out.println(cl.getChannel());
		System.out.println(cl.getStatus().getValue());
		
		// Crea una respuesta y la envía al cliente
		CatalogLookupResponse clr = new CatalogLookupResponse();
		ItemType[] items = new ItemType[1];
		ItemType i = new ItemType();
		i.setBackgroundImage("as");
		i.setCategoryCode(34);
		i.setCategoryName("asd");
		i.setCategorySubSystem("asdf");
		i.setChannel("cha");
		i.setDisabled(Disabled_type1.value2);
		i.setExternalSystemId("extsis");
		i.setImage("img");
		i.setPrizePoints(1234);
		i.setProductCode(43);
		i.setShortDescription("short!");
		i.setType("typ");
		items[0] = i;
		clr.setItem(items);
		return clr;
	}

}

Este método para pasar los parámetros lo aprendí gracias a la respuesta de Nelson Minar a la pregunta How do I pass data from Handler to the service? que hizo Konstantinos Margaritis (http://www.mail-archive.com/axis-user@xml.apache.org/msg20035.html) en la lista axis-user http://ws.apache.org/axis/mail.html.

6. Conclusión
Es enredado leer los encabezados en Axis2 cuando usamos el esqueleto que genera la herramienta WSDL2Java o el Eclipse. Esto es así porque esa herramienta pretende ocultar las complicaciones de SOAP al programador que la utilice. Pero una vez configurado el handler podemos leero todos los encabezados y pasarlos a la clase que implementa el servicio.


Responses

  1. Hola camilotorresf:

    he leido tu articulo y buscado por internet pero no he encontrado lo que necesito espero tu me puedas ayudar, yo necestio crear este encabezo para realizar una peticion a un WS .net y el cliente lo estoy haciendo en java y con axis2 utilizando el stub que genera el wsdl2java.

    ya hice mi bloque de encabezado y se agrego y toda va bien solo por una pequeña cosa que no logro hacer.

    mi cliente envia estas uris dentro de soap y wsa :

    y yo necesito estas:

    ya encontré la forma de cambiar la primera linea la cual es:
    xxxxStub._getServiceClient().getOptions().setSoapVersionURI(“http://schemas.xmlsoap.org/soap/envelope/”);

    y ahora mi cliente envia esto:

    ¿sabras como cambiar la uri de wsa?

    saludos y gracias.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: