Creación de un Servicio Web partiendo de un WSDL con Apache CXF

De Dos Ideas.
Saltar a: navegación, buscar

Pasos:

Crear un proyecto Web con Netbeans

Utilizaremos el nombre CXF para el proyecto.

Agregar la librería CXF al proyecto

Como puede ser que no este instalada, deberemos bajarla del sitio Web (http://cxf.apache.org/download.html). Para este proyecto se utilizó la última versión al momento, que es la 2.2.6

El proyecto viene con muchas mas librerías de las que son estrictamente necesarias para el ejemplo. Las libreria de Geronimo no son necesarias para un despliegue en Tomcat, así como las de Jetty, Jettyson y XMLBeans. Dentro del conjunto de librerias se incluye Spring 2.5.6, ya que usa CXF usa intensivamente dicho framework.

Copiar WSDL de ejemplo al proyecto

Para este proyecto, tenemos un WSDL de un servicio Calculadora, que tiene 2 operaciones:

  • dividir (división entera)
  • sumar

La operación dividir necesita 2 parámetros, dividendo y divisor y devuelve 2 valores, cociente y resto. Además debe devolver un mensaje de error en caso que el divisor sea 0.

Dicho archivo lo copiaremos a la raiz del directorio de fuentes.

Generar código java a partir de un documento WSDL

CXF, como también Axis 2, viene con un generador de código que ahorra al usuario la construcción de todas clases necesarias para la implementación de WebServices. En este caso construiremos una tarea Ant para construir dichas clases.

   <path id="cxf.classpath">
       <fileset dir="./lib/CXF-2.2.6">
           <include name="*.jar" />
       </fileset>
   </path>
   
   <target name="create server side classes">
       < java classname="org.apache.cxf.tools.wsdlto.WSDLToJava" fork="true">
           <arg value="-server" />
           <arg value="-validate" />
           <arg value="-impl" />
           <arg value="-d" />
           <arg value="src/java" />
           <arg value="./src/java/CalculadoraService.wsdl" />
           <classpath>
               <path refid="cxf.classpath" />
           </classpath>
       < /java>
   </target>
   
   <target name="create client side classes">
       < java classname="org.apache.cxf.tools.wsdlto.WSDLToJava" fork="true">
           <arg value="-client" />
           <arg value="-validate" />
           <arg value="-p" />
           <arg value="test.dosideas.com" />
           <arg value="-d" />
           <arg value="test" />
           <arg value="./src/java/CalculadoraService.wsdl" />
           <classpath>
               <path refid="cxf.classpath" />
           </classpath>
       < /java>
   </target>

Se definen 2 tareas: la primera para crear las clases del lado servidor y la segunda para crear las clases del cliente. Cabe notar que a esta última le pasamos un package de parámetro. Esto es para evitar el clashing de clases entre la parte cliente y la parte servidora. En ambos casos estas tareas Ant generarán las clases involucradas en el Web Service y una serie de archivos, incluyendo la clase que contendrá la lógica de negocio, la cual vendrá con implementaciones dummy.

Implementar lógica de negocio

De las clases generadas, la única que tendremos que implementar es la que tiene el sufijo Impl (CalculadoraImpl), la cual a su vez llamará a un objeto de negocio.

   @javax.jws.WebService(
                         serviceName = "CalculadoraService",
                         portName = "CalculadoraSoapPort",
                         targetNamespace = "http://www.dosideas.com/cursos/calculadora",
                         wsdlLocation = "file:./src/java/CalculadoraService.wsdl",
                         endpointInterface = "com.dosideas.cursos.calculadora.Calculadora")
   
   public class CalculadoraImpl implements Calculadora {
       private static final Logger LOG = Logger.getLogger(CalculadoraImpl.class.getName());
       
       protected CalculadoraBo calculadoraBo = null;
       
       public CalculadoraBo getCalculadoraBo() {
           return calculadoraBo;
       }
       
       public void setCalculadoraBo(CalculadoraBo calculadoraBo) {
           this.calculadoraBo = calculadoraBo;
       }
       
       public com.dosideas.cursos.calculadora.DivisionRespuesta dividir(DivisionSolicitud parameters) throws DivisionPorCero    { 
           LOG.info("Executing operation dividir");
           System.out.println(parameters);
           try {
               int dividendo = parameters.getDividendo();
               int divisor = parameters.getDivisor();
               int cociente = calculadoraBo.dividir(dividendo, divisor);
               int resto = calculadoraBo.resto(dividendo, divisor);
               com.dosideas.cursos.calculadora.DivisionRespuesta _return = new DivisionRespuesta();
               _return.setCociente(cociente);
               _return.setResto(resto);
               return _return;
           } catch (ArithmeticException ex) {
               /* Hay que completar todos los datos de la excepción de negocio */
               DivisionPorCero divisionPorCero = new DivisionPorCero("DivisionPorCeroError", "DivisionPorCeroError");
               throw divisionPorCero;
           } catch (Exception ex) {
               ex.printStackTrace();
               throw new RuntimeException(ex);
           }
       }
       
       public com.dosideas.cursos.calculadora.SumaRespuesta sumar(SumaSolicitud parameters) { 
           LOG.info("Executing operation sumar");
           System.out.println(parameters);
           try {
               com.dosideas.cursos.calculadora.SumaRespuesta _return = new SumaRespuesta();
               _return.setNumero(calculadoraBo.sumar(parameters.getNumero1(), parameters.getNumero2()));
               return _return;
           } catch (Exception ex) {
               ex.printStackTrace();
               throw new RuntimeException(ex);
           }
       }
   }

Luego se crea la clase con la lógica de negocio, la cual después se inyectará en la clase Skeleton via Spring

   public class CalculadoraBo {
       public int sum(int n1, int n2) {
           return n1 + n2;
       }
       
       public int divide(int n1, int n2){
           return n1 / n2;
       }
       
       public int modulo(int n1, int n2){
           return n1 % n2;
       }
   }

Configurar descriptores de Spring

   <import resource="classpath:META-INF/cxf/cxf.xml" />
   <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
   <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
   
   <bean id="calculadoraBo" class="com.dosideas.business.CalculadoraBo" />
   
   <bean id="calculadoraImpl" class="com.dosideas.cursos.calculadora.CalculadoraImpl">
       <property name="calculadoraBo" ref="calculadoraBo" />
   </bean>
   
   <jaxws:endpoint id="calculadoraService"
                   address="/calculadoraService"
                   wsdlLocation="CalculadoraService.wsdl"
                   implementor="#calculadoraImpl">
   </jaxws:endpoint>

Es aquí donde configuramos el servicio Web propiamente. Se configura la url donde estará publicado, el WSDL que implementa, y la implementación.

Configurar web.xml

   <context-param>
       <param-name>contextConfigLocation</param-name>
   <param-value>/WEB-INF/applicationContext.xml</param-value>
   </context-param>
   <listener>
       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
   </listener>
   <servlet>
       <servlet-name>CXFServlet</servlet-name>
       <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
   </servlet>
   <servlet-mapping>
       <servlet-name>CXFServlet</servlet-name>
       <url-pattern>/*</url-pattern>
   </servlet-mapping>

Recursos

WSDL de Calculadora Service

Existe un pequeño problema con CXF parte cliente, por que se cambió el WSDL para subsanar ese problema. Dicho problema consiste en que el cliente no escribe correctamente los namespaces en los elementos del mensaje de solicitud. Este error se subsana seteando el WSDL con el atributo elementFormDefault="unqualified" en el schema.

   <wsdl:definitions  name="CalculadoraServiceDefinitions" targetNamespace="http://www.dosideas.com/cursos/calculadora" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:calc="http://www.dosideas.com/cursos/calculadora" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
       <wsdl:types>
           <xs:schema attributeFormDefault="unqualified" elementFormDefault="unqualified" targetNamespace="http://www.dosideas.com/cursos/calculadora">
               <xs:element name="divisionSolicitud">
                   <xs:complexType>
                       <xs:sequence>
                           <xs:element name="dividendo" type="xs:int"/>
                           <xs:element name="divisor" type="xs:int"/>
                       </xs:sequence>
                   </xs:complexType>
               </xs:element>
               <xs:element name="divisionRespuesta">
                   <xs:complexType>
                       <xs:sequence>
                           <xs:element name="cociente" type="xs:int"/>
                           <xs:element name="resto" type="xs:int"/>
                       </xs:sequence>
                   </xs:complexType>
               </xs:element>
               <xs:element name="divisionPorCero" type="xs:string"/>
               <xs:element name="sumaSolicitud">
                   <xs:complexType>
                       <xs:sequence>
                           <xs:element name="numero1" type="xs:int"/>
                           <xs:element name="numero2" type="xs:int"/>
                       </xs:sequence>
                   </xs:complexType>
               </xs:element>
               <xs:element name="sumaRespuesta">
                   <xs:complexType>
                       <xs:sequence>
                           <xs:element name="numero" type="xs:int"/>
                       </xs:sequence>
                   </xs:complexType>
               </xs:element>
           </xs:schema>
       </wsdl:types>
       <wsdl:message name="divisionSolicitud">
           <wsdl:part name="parameters" element="calc:divisionSolicitud">
           </wsdl:part>
       </wsdl:message>
       <wsdl:message name="divisionRespuesta">
           <wsdl:part name="parameters" element="calc:divisionRespuesta">
           </wsdl:part>
       </wsdl:message>
       <wsdl:message name="divisionPorCero">
           <wsdl:part name="divisionPorCero" element="calc:divisionPorCero">
           </wsdl:part>
       </wsdl:message>
       <wsdl:message name="sumarResponse">
           <wsdl:part name="parameters" element="calc:sumaRespuesta">
           </wsdl:part>
       </wsdl:message>
       <wsdl:message name="sumarRequest">
           <wsdl:part name="parameters" element="calc:sumaSolicitud">
           </wsdl:part>
       </wsdl:message>
       <wsdl:portType name="Calculadora">
           <wsdl:operation name="dividir" parameterOrder="parameters">
               <wsdl:input message="calc:divisionSolicitud">
               </wsdl:input>
               <wsdl:output message="calc:divisionRespuesta">
               </wsdl:output>
               <wsdl:fault name="divisionPorCero" message="calc:divisionPorCero">
               </wsdl:fault>
           </wsdl:operation>
           <wsdl:operation name="sumar">
               <wsdl:input message="calc:sumarRequest">
               </wsdl:input>
               <wsdl:output message="calc:sumarResponse">
               </wsdl:output>
           </wsdl:operation>
       </wsdl:portType>
       <wsdl:binding name="CalculadoraServiceSoapBinding" type="calc:Calculadora">
           <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
           <wsdl:operation name="dividir">
               <soap:operation soapAction="http://www.dosideas.com/cursos/calculadora/dividir" style="document"/>
               <wsdl:input>
                   <soap:body parts="parameters" use="literal"/>
               </wsdl:input>
               <wsdl:output>
                   <soap:body parts="parameters" use="literal"/>
               </wsdl:output>
               <wsdl:fault name="divisionPorCero">
                   <soap:fault name="divisionPorCero" use="literal"/>
               </wsdl:fault>
           </wsdl:operation>
           <wsdl:operation name="sumar">
               <soap:operation soapAction="http://www.dosideas.com/cursos/calculadora/sumar" style="document" />
               <wsdl:input>
                   <soap:body parts="parameters" use="literal"/>
               </wsdl:input>
               <wsdl:output>
                   <soap:body parts="parameters" use="literal"/>
               </wsdl:output>
           </wsdl:operation>
       </wsdl:binding>
       <wsdl:service name="CalculadoraService">
           <wsdl:port name="CalculadoraSoapPort" binding="calc:CalculadoraServiceSoapBinding">
               <soap:address location="http://localhost:8080/CXF/calculadoraService"/>
           </wsdl:port>
       </wsdl:service>
   </wsdl:definitions>
  

Proyecto de ejemplo para descargar

Proyecto Ejemplo Java 1.5 y CXF 2.2.6 para Weblogic

Proyecto Ejemplo con Java 1.5 y CXF 2.2.4

Ver también