lunes, 24 de febrero de 2014

Implementando un cliente para un servicio en JAX-WS

Luego de ver como se implementa un servicio con JAX-WS y Metro, y desplegarlo en un contenedor de aplicaciones ahora quiero mostrarles cómo implementar un cliente para el servicio ya desplegado.

Para este caso creamos un proyecto en Maven, no usando el archetype de webapp, pues lo que queremos es un proyecto que nos genere un jar.

Una vez que tenemos el proyecto debemos modificar el pom para que queda algo como lo siguiente, ustedes hagan los cambios necesarios claro.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>jaxws4</groupId>
    <artifactId>jaxws4</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.ws</groupId>
            <artifactId>jaxws-rt</artifactId>
            <version>2.2.8</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-xjc</artifactId>
            <version>2.2.6</version>
        </dependency>
        <dependency>
            <groupId>org.codehaus.plexus</groupId>
            <artifactId>plexus-io</artifactId>
            <version>2.0.6</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.0.2</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.jvnet.jax-ws-commons</groupId>
                <artifactId>jaxws-maven-plugin</artifactId>
                <version>2.1</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>wsimport</goal>
                        </goals>
                        <configuration>
                            <wsdlDirectory>${basedir}/src/main/resources/wsdl</wsdlDirectory>
                            <wsdlFiles>
                                <wsdlFile>helloPersonService.wsdl</wsdlFile>
                            </wsdlFiles>
                            <packageName>cu.uci.cdae.ws.client</packageName>
                            <sourceDestDir>${basedir}/target/generated-sources/</sourceDestDir>
                            <keep>true</keep>
                            <bindingFiles>
                                <bindingFile>${basedir}/src/main/resources/wsdl/binding.xml</bindingFile>
                                <bindingFile>${basedir}/src/main/resources/wsdl/jaxb-binding.xml</bindingFile>
                            </bindingFiles>
                        </configuration>
                        <phase>generate-sources</phase>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>com.sun.xml.bind</groupId>
                        <artifactId>jaxb-xjc</artifactId>
                        <version>2.1.12</version>
                    </dependency>
                    <dependency>
                        <groupId>com.sun.xml.ws</groupId>
                        <artifactId>jaxws-rt</artifactId>
                        <version>2.1.7</version>
                        <scope>compile</scope>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>
</project>
En la carpeta resources ponemos tanto el WSDL como el XSD en la misma ubicación, que como vimos en la entrada para crear la implementación del servicio y además en este caso añadimos 2 ficheros en la misma carpeta del WSDL. Estos ficheros son binding.xml y jaxb-binding.xml binding.xml
<bindings
        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
        wsdlLocation="./helloPersonService.wsdl"
        xmlns="http://java.sun.com/xml/ns/jaxws">
    <!-- Disable default wrapper style -->
    <enableWrapperStyle>false</enableWrapperStyle>

</bindings>
jaxb-binding.xml
<jaxb:bindings version="2.1" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
               xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <jaxb:globalBindings generateElementProperty="false" />
</jaxb:bindings>
Ahora solo tenemos que ejecutar el comando de Maven: mvn clean compile Y ya tendremos generadas las clases para consumir el servicio. La clase que implementará el cliente del servicio es realmente sencilla y se muestra a continuación:
package cu.uci.cdae.ws.client;

import javax.xml.ws.WebServiceRef;


public class clientews {
    @WebServiceRef(wsdlLocation = "wsdl/helloPersonService.wsdl")

    public static void main(String[] args) {
        try {

            HelloPersonService fg = new HelloPersonService();
            HelloPersonServicePortType port = fg.getHelloPersonServicePort();
   
      /* Esto se usa cuando el endpoint del WSDL no es el mismo que el del servicio real,ENDPOINT es un String que contiene el endpoint real
   ((BindingProvider)port).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, ENDPOINT);
   */
   
            HelloPersonServiceRequestType request = new HelloPersonServiceRequestType();
            PersonType persona = new PersonType();

            long inicio = System.currentTimeMillis();
            persona.setFirstName("Jorge");
            persona.setLastName("Infante Osorio");
            request.setPerson(persona);

            HelloPersonServiceResponseType response = port.greetPerson(request);

            System.out.println(response.getGreetings());
            System.out.println("La demora es de: " + (System.currentTimeMillis() - inicio));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}


Y de esta manera ya tenemos un cliente funcional para consumir nuestro servicio web. Es bueno aclarar que como estamos usando maven podemos usar cualquier IDE siempre y cuando lo tengamos configurado apropiadamente para que nos resuelva las dependencias vía maven.

Espero les sea de utilidad.

Despliegue de un servicio JAX-WS en tomcat y en el AS de WSO2

Desplegar un servicio jax-ws es realmente sencillo. Si seguimos los pasos de la entrada anterior, lo que tenemos que hacer ahora es ir a la carpeta  target y buscar el .war, en nuestro caso sería hello_person.war porque es el nombre que especificamos como final en el fichero pom.xml.

Para hacerlo en Tomcat:

Nos vamos donde tengamos un tomcat instalado y copiamos el fichero en [tomcat_install]\webapps

Al iniciar el tomcat podemos ir a esta ubicación:  http://localhost:8080/hello_person/helloPersonService y veremos lo siguiente:

Usando la ubicación del WSDL pueden probar el servicio usando SOAPUI, tal y como se ha mostrado en esta otra entrada.

Para hacerlo en el Application Server de WSO2:

Lo primero es tener una instancia corriendo de esta herramienta  e ir a la pestaña Main/Applications/Add/JAX-WS/JAX-RS, ahí veremos lo siguiente:
NOTA: como la implementación no es con CXF no la detecta como un servicio JAX-WS

Damos clic en Examinar, seleccionamos nuestra aplicación y le damos al botón Upload.

Una vez cargada podemos ver como se muestra:

Como en mi caso el offset está en 2 la url para acceder al servicio es la siguiente:

Espero les sea de utilidad

domingo, 23 de febrero de 2014

Introducción a JAX-WS con Maven.

Hace poco estaba leyendo un pdf de WSO2 sobre sus recomendaciones para los desarrolladores que usan esta plataforma en su día a día y mencionaban con mucho hincapié a JAX-WS y JAX-RS como las APIs preferidas por los desarrolladores para los temas de servicios web y REST.

Además de soportar los servicios axis2 y los servicios de acceso a datos, WSO2 permite el despliegue de servicios JAX-WS con la implementación de CXF y JAX-RS. Entre las ventajas que brinda la plataforma se pueden mencionar:

  • Facilidad de acceso a los almacenes de usuarios a través de las APIs de WSO2.
  • Facilidad de acceso a la API del registro de WSO2 para la persistencia de información.
  • Autorización de grano fino usando el Identity Server de WSO2.
  • Una interfaz gráfica amigable para la gestión de las aplicaciones que contienen los servicios desplegados.
  • Despliegue y actualización en caliente de los servicios web.

Para comenzar a adentrarnos en JAX-WS lo haremos primero con Metro, luego pasaremos a CXF y veremos como desplegar estas aplicaciones en el AS con las facilidades antes mencionadas.

En esta entrada veremos cómo implementar un servicio web en JAX-WS con la ayuda de Maven.
Se implementa el escenario en el cual tenemos un WSDL ya diseñado y queremos implementar el servicio a partir de dicho WSDL. El cual mostramos a continuación:

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions
        name="helloPersonService"
        targetNamespace="http://example.nl/hellopersonservice/1.0"
        xmlns:tns="http://example.nl/hellopersonservice/1.0"
        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
        >
    <wsdl:types>
        <xsd:schema targetNamespace="http://example.nl/hellopersonservice/1.0">
            <xsd:import schemaLocation="../xsd/helloPersonService.xsd"
                        namespace="http://example.nl/hellopersonservice/1.0"/>
        </xsd:schema>
    </wsdl:types>
    <wsdl:message name="HelloPersonServiceRequest">
        <wsdl:part name="HelloPersonServiceRequest" element="tns:HelloPersonServiceRequest"/>
    </wsdl:message>
    <wsdl:message name="HelloPersonServiceResponse">
        <wsdl:part name="HelloPersonServiceResponse" element="tns:HelloPersonServiceResponse"/>
    </wsdl:message>
    <wsdl:portType name="HelloPersonServicePortType">
        <wsdl:operation name="greetPerson">
            <wsdl:input name="HelloPersonServiceRequest" message="tns:HelloPersonServiceRequest"/>
            <wsdl:output name="HelloPersonServiceResponse" message="tns:HelloPersonServiceResponse"/>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="HelloPersonServiceBinding" type="tns:HelloPersonServicePortType">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="greetPerson">
            <soap:operation style="document" soapAction="http://example.nl/HelloPersonService/greetPerson"/>
            <wsdl:input name="HelloPersonServiceRequest">
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output name="HelloPersonServiceResponse">
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="HelloPersonService">
        <wsdl:port name="HelloPersonServicePort" binding="tns:HelloPersonServiceBinding">
            <soap:address location="/service/helloPersonService" />
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

Y como este wsdl referencia a un XSD también les mostramos el XSD:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            elementFormDefault="qualified"
            xmlns="http://example.nl/hellopersonservice/1.0"
            targetNamespace="http://example.nl/hellopersonservice/1.0">
    <xsd:element name="HelloPersonServiceRequest" type="HelloPersonServiceRequestType"/>
    <xsd:element name="HelloPersonServiceResponse" type="HelloPersonServiceResponseType"/>

    <xsd:complexType name="HelloPersonServiceRequestType">
        <xsd:element name="Person" type="PersonType"/>
    </xsd:complexType>

    <xsd:complexType name="HelloPersonServiceResponseType">
        <xsd:element name="Greetings" type="xsd:string"/>
    </xsd:complexType>

    <xsd:complexType name="PersonType">
        <xsd:sequence>
            <xsd:element name="FirstName" type="xsd:string"/>
            <xsd:element name="LastName" type="xsd:string"/>
        </xsd:sequence>
    </xsd:complexType>
</xsd:schema>


Comencemos a implementar nuestro servicio web usando JAX-WS.

Paso 1: Crear una aplicación web en Maven. Para aquellos que no saben les recomiendo este enlace.

Paso 2: incluir la dependencia de jax-ws en su fichero pom.xml. les muestro el pom.xml completo del proyecto


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>jaxws3</groupId>
  <artifactId>jaxws3</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>jaxws3 Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
      <dependency>
          <groupId>javax</groupId>
          <artifactId>javaee-web-api</artifactId>
          <version>6.0</version>
          <scope>provided</scope>
      </dependency>
   <!-- Dependencia de JAX-WS-->
      <dependency>
          <groupId>com.sun.xml.ws</groupId>
          <artifactId>jaxws-rt</artifactId>
          <version>2.2.8</version>
          <scope>compile</scope>
      </dependency>
      <dependency>
          <groupId>com.sun.xml.bind</groupId>
          <artifactId>jaxb-xjc</artifactId>
          <version>2.2.6</version>
      </dependency>
      <dependency>
          <groupId>org.codehaus.plexus</groupId>
          <artifactId>plexus-io</artifactId>
          <version>2.0.6</version>
      </dependency>
  </dependencies>
  <build>
    <!-- Nombre que tendra la aplicacion web-->
    <finalName>hello_person</finalName>
      <plugins>
          <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-compiler-plugin</artifactId>
              <version>2.0.2</version>
              <configuration>
                  <source>1.7</source>
                  <target>1.7</target>
              </configuration>
          </plugin>
          <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-war-plugin</artifactId>
              <version>2.2</version>
              <configuration>
                  <failOnMissingWebXml>false</failOnMissingWebXml>
              </configuration>
          </plugin>
           
          <plugin>
              <groupId>org.mortbay.jetty</groupId>
              <artifactId>jetty-maven-plugin</artifactId>
              <configuration>
                  <webAppConfig>
                      <contextPath>hello_person</contextPath>
                  </webAppConfig>
                  <connectors>
                      <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
                          <port>8083</port>
                      </connector>
                  </connectors>
              </configuration>
          </plugin>

          <plugin>
              <groupId>org.codehaus.mojo</groupId>
              <artifactId>build-helper-maven-plugin</artifactId>
              <version>1.8</version>
              <executions>
                  <execution>
                      <id>add-source</id>
                      <phase>generate-sources</phase>
                      <goals>
                          <goal>add-source</goal>
                      </goals>
                      <configuration>
                          <sources>
                              <source>${basedir}/target/generated/src/main/java</source>
                          </sources>
                      </configuration>
                  </execution>
              </executions>
          </plugin>
          <!-- Plugin para generar el codigo a partir del WSDL del servicio web-->
          <plugin>
              <groupId>org.jvnet.jax-ws-commons</groupId>
              <artifactId>jaxws-maven-plugin</artifactId>
              <version>2.1</version>
              <configuration>
                  <sei>cu.uci.cdae.hello_person.service.HelloPersonServiceImpl</sei>
                  <wsdlDirectory>${basedir}/src/main/resources/wsdl</wsdlDirectory>
                  <packageName>cu.uci.cdae.hello_person.service.generated</packageName>
                  <keep>true</keep>
                  <sourceDestDir>${basedir}/target/generated/src/main/java</sourceDestDir>
              </configuration>
              <executions>
                  <execution>
                      <goals>
                          <goal>wsimport</goal>
                      </goals>
                  </execution>
              </executions>
              <dependencies>
                  <dependency>
                      <groupId>com.sun.xml.bind</groupId>
                      <artifactId>jaxb-xjc</artifactId>
                      <version>2.1.12</version>
                  </dependency>
                  <dependency>
                      <groupId>com.sun.xml.ws</groupId>
                      <artifactId>jaxws-rt</artifactId>
                      <version>2.1.7</version>
                      <scope>compile</scope>
                  </dependency>
              </dependencies>
          </plugin>
      </plugins>
  </build>
</project>

Paso 3: incluir el plugin para Maven de JAX-WS, el cual pueden ver en el pom.xml. Este plugin nos permite generar el código del servicio web a partir del WSDL. Su configuración es bastante sencilla y debemos especificarle la ubicación del wsdl, el paquete donde se generará el código, si mantenemos el fuente generado pues por defecto se borran los .java y solo quedan los compilados, y por último el destino de donde pondremos el fuente generado.


Paso 4: crear un carpeta en [proyecto]\src\main\resources\ con el nombre wsdl que es donde se pondrá el wsdl diseñado y otra xsd que es donde se pondrá el xsd referenciado por el wsdl.

Paso 5: debemos ir a [proyecto]\ src\main\webapp\WEB-INF\ y crear 2 ficheros sun-jaxws.xml y web.xml cuyo contenido es el siguiente.

fichero sun-jaxws.xml
<?xml version="1.0" encoding="UTF-8"?>
<endpoints
        xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime'
        version='2.0'>
    <endpoint
            name='helloPersonService'
            implementation='cu.uci.cdae.hello_person.service.HelloPersonServiceImpl'
            url-pattern='/helloPersonService' />
</endpoints>

fichero web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    <listener>
        <listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
    </listener>
    <servlet>
        <description>JAX-WS endpoint</description>
        <display-name>The JAX-WS servlet</display-name>
        <servlet-name>jaxws</servlet-name>
        <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>jaxws</servlet-name>
        <url-pattern>/helloPersonService</url-pattern>
    </servlet-mapping>
</web-app>


En sun-jaxws.xml definimos el endpoint que usaremos, el patrón para la URL y la clase que implementa este endpoint.
En web.xml definimos el Servlet y el mapeo que usaremos.

Llegado este punto podemos ir a la raíz del proyecto y ejecutar el siguiente comando Maven:
mvn clean compile

En la carpeta /target/generated/src/main/java y en el paquete cu.uci.cdae.hello_person.service.generated tendremos el código generado automáticamente.

Paso 5: implementar la lógica de negocio del servicio.
Si revisaron el WSDL verán que recibimos un objeto persona que tiene nombre y apellidos y se devuelve un String.

La clase que implementa el  servicio es la siguiente:

package cu.uci.cdae.hello_person.service;

import cu.uci.cdae.hello_person.service.generated.HelloPersonServiceRequestType;
import cu.uci.cdae.hello_person.service.generated.HelloPersonServiceResponseType;

import javax.jws.WebService;

@WebService(endpointInterface = "cu.uci.cdae.hello_person.service.generated.HelloPersonServicePortType")
public class HelloPersonServiceImpl implements cu.uci.cdae.hello_person.service.generated.HelloPersonServicePortType {

    @Override
    public HelloPersonServiceResponseType greetPerson(HelloPersonServiceRequestType helloPersonServiceRequest) {
        HelloPersonServiceResponseType helloPersonServiceResponse = new HelloPersonServiceResponseType();
        helloPersonServiceResponse.setGreetings("Hello " + helloPersonServiceRequest.getPerson().getFirstName() + " " + helloPersonServiceRequest.getPerson().getLastName() + "!");
        return helloPersonServiceResponse;
    }

}

Como pueden ver la implementación es realmente limpia. La clase es anotada para que sea contemplada como uun servicio web y especificamos la interfaz del endpoint. Esta clase debe implementar dicha interfaz y acepta  un objeto que contiene la estructura de la persona definida en el XSD que vimos al inicio.

Lo que nos queda ahora es empaquetar nuestro servicio usando el siguiente comando de maven:
mvn clean package

Y el war generado desplegarlo en un Tomcat o en el AS de WSO2

Espero les sea de utilidad

jueves, 13 de febrero de 2014

WSO2 API Manager. Introducción.


El producto WSO2 API Manager, de la suite de WSO2 ha estado tomando bastante auge gracias al movimiento hacia el desarrollo de servicios RESTful y la construcción de APIs.

Este producto permite, entre otras cosas:
  1. Publicar las APIs y gestionar su ciclo de vida.
  2. Compartir información sobre las APIs.
  3. Brindar un mecanismo para la calificación de las APIs.
  4. Brindar un mecanismo de descubrimiento a los desarrolladores que vayan a actuar como consumidores de las APIs.
  5. Monitorear el consumo de las APIs, sus tiempos de respuesta, cantidad de accesos y quien las consume.
  6. Filtrar la cantidad de accesos por unidad de tiempo y garantizar la seguridad del acceso a las APIs, basados en políticas de seguridad.
  7. Brindar un mecanismo para que los desarrolladores se puedan registrar y subscribirse a las APIs, así como generar claves de acceso necesarias para su consumo.

Para lograr estas funcionalidades y otras más el producto APIM ha establecido la siguiente distribución de sus componentes.



Una descripción de cada componente se puede encontrar en el siguiente blog

La utilidad de la herramienta se puede apreciar a través del siguiente escenario:

Se ha  desarrollado una aplicación que se encarga de la venta de pizzas por internet. Los desarrolladores de la aplicación se han dado cuenta que no solo pueden hacer dinero a través de la interfaz gráfica de la aplicación si no también con sus funcionalidades, así que han identificado funcionalidades que se pueden exponer como APIs, ellas son:
  1. Listar el menú de las pizzas en venta junto con sus descripciones y precios.
  2. Ordenar pizzas online generando un id de la orden para su posterior rastreo.
  3. Monitorear la entrega de las pizzas a partir de los id de las órdenes.
Su idea es que con estas funcionalidades otros sistemas podrán incluir la venta de pizzas a partir del consumo de estas APIs y ellos podrán cobrar por dicho consumo, además de controlar la forma de acceso garantizando que no se les escape un centavo :-) , y monitorizar las mismas para ver si su iniciativa tiene éxito o no.

El desarrollo de la solución para por los siguientes pasos:
  1. Exponer las funcionalidades como APIs.
  2. Publicar las APIs en un sistema que permita que otros desarrolladores las descubran.
  3. Los desarrolladores se registran en el sistema que publica las APIs para descubrirlas, suscribirse y solicitar acceso a las mismas.
  4. El sistema a solicitud de los desarrolladores genera claves de acceso a las APIs para que  las puedan consumir de una manera segura.
  5. Llegados a este paso los desarrolladores ya podrán probar el consumo de las APIs, crear su propia aplicación que consuma las APIs y comenzar a acceder a sus funcionalidades y datos. La seguridad del acceso se garantiza a través de Oauth 2.0
  6. Por su parte los creadores de las APIs podrán monitorear su consumo y monetizar el mismo.

Para aquellos que ya quieren ver cómo implementar este escenario les recomiendo pasar por este blog. Y se pueden llevar una idea de como funciona a través de la siguiente imagen:


En la próxima entrada implementaremos estos pasos usando las siguientes herramientas:


En el AS hostearemos una aplicación web que contendrá las APIs, así como la solución final.
En el APIM manejaremos todos los requerimientos relacionados con las APIs.
En el BAM guardaremos las estadísticas de consumo de las APIs.

miércoles, 12 de febrero de 2014

Servicios de Datos en WSO2 con respuesta del tipo JSON.

En una entrada pasada  vimos como exponer datos almacenamos en una base de datos relacionar a través de un servicio de acceso a datos usando la suite de WSO2.

Luego vimos como exponer este mismo servicio pero de una manera RESTful, lo que en ese momento solo se exponía  el XML de la respuesta.

Para terminar se hizo otra entrada donde se mostraba como crear un servicio usando jersey para consumir el servicio de acceso a datos expuesto de una manera RESTful.

La razón de esta entrada entonces es mostrar como los datos expuestos por este servicio se pueden exponer en JSON y no en XML.

Los pasos son los siguientes:

En el fichero axis2.xml del Application Server agregamos el parámetro “httpContentNegotiation” con el valor true.




Lo mismo hacemos en el fichero axis2_client.xml





Llegado a este punto si consumimos el servicio como antes pues seguiremos viendo la misma respuesta, vean usando curl:

O usando el RESTclient en Firefox:


Probemos ahora a agregar un encabezado del tipo "Accept:application/json"

Si usamos curl:



Vemos que efectivamente la respuesta viene en JSON. Lo mismo nos pasa cuando usamos el cliente en Firefox:




Como ven de una manera muy fácil podemos exponer nuestros servicios web de forma tal que los datos se muestren como JSON.

Espero les sea de utilidad.

sábado, 8 de febrero de 2014

La comunidad de Bonita en español ya puede postear en su idioma.


Desde ayer en el blog de bonita se puede leer una noticia sumamente importante para los desarrolladores BPM de habla hispana: Ya podemos postear e intercambiar información en español relacionada con la herramienta Bonita.
Una señal de que estamos ganando un espacio importante en los temas de BPM y también de SOA.

viernes, 7 de febrero de 2014

Interés en WSO2 desde América Latina.

Aun cuando WSO2 es relativamente poco conocida en comparación con otras empresas dedicadas a los temas de SOA/BPM/CEP/CloudComputing ya va tomando batante auge y lo demuestran sus casos de éxito, los múltiples eventos que hacen y en los otros que participan.

En América Latina pues igual se va imponiendo esta suite y también en el mundo de habla hispana, y ya existen iniciativas de cursos en español y de traducciones de la UI de la suite a nuestro idioma. En próximas entradas veremos algo de esto último.

En este blog el tráfico también se ha ido incrementando notablemente en estos últimos meses tal y como muestra la siguiente imagen.



 Vean como existe un interés en WSO2 en nuestros países del continente aunque España sigue siendo el que más accesos brinda, seguido de Mexico y Cuba, en donde tenemos un equipo bastante fuerte en cuanto a conocimientos en el uso y desarrollo de soluciones de integración usando esta suite.

WSO2 BAM/CEP: Escenario 1 de aplicación para eventos complejos.


El BAM de WSO2 en su versión 2.4.0 ya viene con las funcionalidades del CEP de WSO2 incluidas.
Esto permite no solo capturar eventos para mostrarlos posteriormente usando el dashboard del BAM,si no que los eventos se capturan en tiempo real usando las funcionalidades del CEP y se pueden procesar para la toma de decisiones también en tiempo real.

Como ejemplo les muestro una gráfica generada por el BAM del flujo de eventos recibidos.


Y debajo pueden ver como es el comportamiento de la cantidad de request/response/fault de los eventos recibidos.


  El escenario implementado para esta entrada es realmente sencillo a manera de "Hola Mundo" y estos son los pasos de manera general: 
  • Configurar el AS de WSO2 para que publique los eventos del consumo de los servicios para el BAM. 
  • Invocar a un servicio desplegado en el AS para que el BAM reciba un evento, de esta manera se crea automáticamente un Stream tal y como se muestra en la siguiente figura.


  En este caso se crea automáticamente el Stream de nombre “bam_service_data_publisher” versión 1.0.0 
  • Crear un plan de ejecución que extraiga de cada evento que arribe el tiempo de respuesta del servicio y si es menor que determinada cantidad de milisegundos inserte el evento en un nuevo Stream, usando los atributos especificados.




Como pueden ver uso el Stream de entrada y si en un evento el response_time es mayor que 1s se selecciona el nombre del servicio, el nombre de la operación y el tiempo de respuesta y se insertan en un Stream nuevo.
Este Stream nuevo la herramienta nos permite crearlo muy fácilmente y es el que se llama “OutMediationStatsStream”
  
  • Creamos un adaptador de salida de tipo email.


Que tiene la siguiente configuración.

  • Ahora se crea un formateador de eventos, que usará el adaptador de salida previamente creado y nos permite formatear el evento de salida tal y como se muestra en las siguientes imágenes.



  • Ahora ya se puede probar la configuración creada, para ello solo es necesario invocar a un servicio desplegado en el AS y observar los resultados.
En nuestro caso usamos el SOAPUI para generar una prueba de carga.

Y estamos usando el servicio Echo, con la operación echoString.
Les muestro nuevamente las gráficas generadas:


Y un ejemplo de un correo enviado:
Se ha detectado el consumo de servicio echo, en la operacion echoString con un tiempo de respuesta de 2502 el cual se encuentra por encima del valor establecido

En otras entradas estaremos viendo planes de ejecución más complejos que usen diferentes tipos del filtros y ventanas para capturar escenarios complejos, así como diferentes formas de generar los eventos y de generar las salidas como respuesta a los eventos complejos.



jueves, 6 de febrero de 2014

WSO2 BAM: Solucionando un error del tipo Broken Pipe.

En algunas pruebas de alto rendimiento realizadas con el WSO2 BAM versión 2.4.0 podemos toparnos con un error del tipo broken pipe. Y quisiera dejarles la solución del mismo.

Antes les dejo los logs generados para que les sirva en la identificación del problema.


TID: [0] [BAM] [2014-02-03 10:17:39,593] ERROR {org.apache.hadoop.mapred.MapTask} -  IO error in map input file file:/opt/wso2bam-2.4.0/repository/data/hive/warehouse-1234/mappingjmxdatatable {org.apache.hadoop.mapred.MapTask}
TID: [0] [BAM] [2014-02-03 10:17:42,063]  WARN {org.apache.hadoop.mapred.LocalJobRunner} -  job_local_0001 {org.apache.hadoop.mapred.LocalJobRunner}
java.io.IOException: IO error in map input file file:/opt/wso2bam-2.4.0/repository/data/hive/warehouse-1234/mappingjmxdatatable
        at org.apache.hadoop.mapred.MapTask$TrackedRecordReader.moveToNext(MapTask.java:242)
        at org.apache.hadoop.mapred.MapTask$TrackedRecordReader.next(MapTask.java:216)
        at org.apache.hadoop.mapred.MapRunner.run(MapRunner.java:48)
        at org.apache.hadoop.mapred.MapTask.runOldMapper(MapTask.java:435)
        at org.apache.hadoop.mapred.MapTask.run(MapTask.java:371)
        at org.apache.hadoop.mapred.LocalJobRunner$Job.run(LocalJobRunner.java:211)
Caused by: java.io.IOException: java.lang.RuntimeException: org.apache.thrift.transport.TTransportException: java.net.SocketException: Broken pipe
        at org.apache.hadoop.hive.io.HiveIOExceptionHandlerChain.handleRecordReaderNextException(HiveIOExceptionHandlerChain.java:121)
        at org.apache.hadoop.hive.io.HiveIOExceptionHandlerUtil.handleRecordReaderNextException(HiveIOExceptionHandlerUtil.java:77)
        at org.apache.hadoop.hive.ql.io.HiveContextAwareRecordReader.doNext(HiveContextAwareRecordReader.java:275)
        at org.apache.hadoop.hive.ql.io.HiveRecordReader.doNext(HiveRecordReader.java:79)
        at org.apache.hadoop.hive.ql.io.HiveRecordReader.doNext(HiveRecordReader.java:33)
        at org.apache.hadoop.hive.ql.io.HiveContextAwareRecordReader.next(HiveContextAwareRecordReader.java:108)
        at org.apache.hadoop.mapred.MapTask$TrackedRecordReader.moveToNext(MapTask.java:236)
        ... 5 more
Caused by: java.lang.RuntimeException: org.apache.thrift.transport.TTransportException: java.net.SocketException: Broken pipe
        at org.apache.hadoop.hive.cassandra.input.ColumnFamilyRowRecordReader$StaticRowIterator.maybeInit(ColumnFamilyRowRecordReader.java:619)
        at org.apache.hadoop.hive.cassandra.input.ColumnFamilyRowRecordReader$StaticRowIterator.computeNext(ColumnFamilyRowRecordReader.java:624)
        at org.apache.hadoop.hive.cassandra.input.ColumnFamilyRowRecordReader$StaticRowIterator.computeNext(ColumnFamilyRowRecordReader.java:556)
        at com.google.common.collect.AbstractIterator.tryToComputeNext(AbstractIterator.java:135)
        at com.google.common.collect.AbstractIterator.hasNext(AbstractIterator.java:130)
        at org.apache.hadoop.hive.cassandra.input.ColumnFamilyRowRecordReader.nextKeyValue(ColumnFamilyRowRecordReader.java:242)
        at org.apache.hadoop.hive.cassandra.input.CassandraHiveRecordReader.nextKeyValue(CassandraHiveRecordReader.java:182)
        at org.apache.hadoop.hive.cassandra.input.CassandraHiveRecordReader.next(CassandraHiveRecordReader.java:75)
        at org.apache.hadoop.hive.cassandra.input.CassandraHiveRecordReader.next(CassandraHiveRecordReader.java:22)
        at org.apache.hadoop.hive.ql.io.HiveContextAwareRecordReader.doNext(HiveContextAwareRecordReader.java:273)
        ... 9 more
Caused by: org.apache.thrift.transport.TTransportException: java.net.SocketException: Broken pipe
        at org.apache.thrift.transport.TIOStreamTransport.write(TIOStreamTransport.java:147)
        at org.apache.thrift.transport.TFramedTransport.flush(TFramedTransport.java:157)
        at org.apache.thrift.TServiceClient.sendBase(TServiceClient.java:65)
        at org.apache.cassandra.thrift.Cassandra$Client.send_get_range_slices(Cassandra.java:686)
        at org.apache.cassandra.thrift.Cassandra$Client.get_range_slices(Cassandra.java:675)
        at org.apache.hadoop.hive.cassandra.input.ColumnFamilyRowRecordReader$StaticRowIterator.maybeInit(ColumnFamilyRowRecordReader.java:583)
        ... 18 more
Caused by: java.net.SocketException: Broken pipe
        at java.net.SocketOutputStream.socketWrite0(Native Method)
        at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:113)
        at java.net.SocketOutputStream.write(SocketOutputStream.java:159)
        at org.apache.thrift.transport.TIOStreamTransport.write(TIOStreamTransport.java:145)
        ... 23 more
TID: [0] [BAM] [2014-02-03 10:17:42,296] ERROR {org.apache.hadoop.hive.ql.exec.ExecDriver} -  Ended Job = job_local_0001 with errors {org.apache.hadoop.hive.ql.exec.ExecDriver}
TID: [0] [BAM] [2014-02-03 10:17:42,302] ERROR {org.apache.hadoop.hive.ql.exec.ExecDriver} -  Error during job, obtaining debugging information... {org.apache.hadoop.hive.ql.exec.ExecDriver}



SOLUCIÓN:

La solución es realmente sencilla. Solo basta localizar el fichero cassandra.yaml y modificar estas 2 líneas:

thrift_framed_transport_size_in_mb: 15
thrift_max_message_length_in_mb: 16

y dejarlas como sigue:

thrift_framed_transport_size_in_mb: 60
thrift_max_message_length_in_mb: 64