Buscar este blog

Mostrando entradas con la etiqueta JAX-WS. Mostrar todas las entradas
Mostrando entradas con la etiqueta JAX-WS. Mostrar todas las entradas

jueves, 28 de julio de 2016

Web service client - Spring cache

This is the third post about cache. The first one was about how to configure persistence cache with Hibernate, and the second one about how to configure a business cache with Spring, both of then by using Infinispan embbede in JBoss.

The objective of this post is to show how to configure the bussines cache with web services clients generated with maven cxf-codegen-plugin. The only special feature here is that you can not use the cache annotations (@Cacheable, @CacheEvict, etc.) because you have not access to the web service client source code. This code is generated by Maven in the compile phase.

In this example I will use the famous currency converter service: http://www.webservicex.com/CurrencyConvertor.asmx?wsdl

The first step is to configure pom.xml in order to parse the WSDL and to generate all needed classes.
<plugin>
 <groupId>org.apache.cxf</groupId>
 <artifactId>cxf-codegen-plugin</artifactId>
 <version>2.7.18</version>
 <executions>
  <execution>
   <id>generate-sources-wsdl</id>
   <phase>generate-sources</phase>
   <configuration>
    <wsdlOptions>
     <wsdlOption>
      <wsdl>${basedir}/src/main/resources/META-INF/wsdl/CurrencyConvertor.wsdl</wsdl>
      <extraargs>
       <extraarg>-xjc-XhashCode</extraarg>
       <extraarg>-xjc-Xequals</extraarg>
       <extraarg>-xjc-XtoString</extraarg>
      </extraargs>
     </wsdlOption>
    </wsdlOptions>
   </configuration>
   <goals>
    <goal>wsdl2java</goal>
   </goals>
  </execution>
 </executions>
 <dependencies>
  <dependency>
   <groupId>org.jvnet.jaxb2_commons</groupId>
   <artifactId>jaxb2-basics</artifactId>
   <version>1.11.1</version>
  </dependency>
 </dependencies>
</plugin>

I have downloaded the wsdl file and have put it inside the project in /resources/META-INF/wsdl directory.

Next, you have to configure de jaxws client, so you could use this client as a managed spring bean. In a context config file (for example, cxf-config.xml) I have this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">


 <bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor">
    <property name="prettyLogging" value="true"/>
 </bean>

 <bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor">
    <property name="prettyLogging" value="true"/>
 </bean>


 <jaxws:client id="currencyConversorClient"
  name="{http://www.webserviceX.NET}CurrencyConvertorSoap12" 
  serviceClass="net.webservicex.CurrencyConvertorSoap"
  address="http://www.webservicex.com/CurrencyConvertor.asmx">

  <jaxws:outInterceptors>
   <ref bean="loggingOutInterceptor" />
  </jaxws:outInterceptors>

  <jaxws:inInterceptors>
   <ref bean="loggingInInterceptor" />
  </jaxws:inInterceptors>
 </jaxws:client>

</beans>

With this configuration you can use the currencyConversorClient injected in other Spring managed beans, for example:
@Autowired    
private net.webservicex.CurrencyConvertorSoap currencyService;

And finally, you have to configure the cache in a declarative way, by using another context config file, as is explained in the reference documentation.
<!-- cache manager and  annotation-driven configuration omited-->

<!-- cache definitions -->
<cache:advice id="currencyConversorWebServiceCacheAdvice" cache-manager="cacheManager">
 <cache:caching cache="currency">
  <cache:cacheable method="conversionRate"/>
 </cache:caching>
</cache:advice>

<aop:config>
 <aop:advisor advice-ref="currencyConversorWebServiceCacheAdvice" pointcut="execution(* net.webservicex.CurrencyConvertorSoap.*(..))"/>
</aop:config>

In this snippet you declare a cache named "currency" over the conversionRate method in the CurrencyConvertorSoapCurrencyConvertorSoap service. Note that this is actually an interface which was generated by Maven.


Updated 1
Ultimately, a cache is just a hashmap like structure which store keys (the methods parameters) and values (the method return). In order to work properly, the cache keys must override the equals and hashcode methods, because two instances of a class with the same properties may not be equals.

So, when creating the web service class, you need to configure the cxf-codegen-plugin to add equals and hashcode methods to the DTOs. You get this by using the extra args tag, and by importing jaxb2-basics dependencies.

The currency example is very basic and the method params are simple types, which don't require this approximation, but for complex scenarios this would be mandatory.

Updated 2
Under some circumstances (I don't know exactly when) you need to add a dependency with org.jboss.ironjacamar.jdbcadapters module in the jboss-deployment-structure.xml file. Otherwise you will get a very confusing error.

Check this post: http://trabajosdesisifo.blogspot.com.es/2016/08/jboss-illegalargumentexception-warning.html

domingo, 16 de agosto de 2015

wsdl2java - Use java.util.Date instead of XMLGregorianCalendar

If you have a web service definition, WSDL, and you want to generate the equivalent java code (either for the client or for the server side) one of the simplest way is by using Apache CXF wsdl2java Maven plugin. Check here: http://cxf.apache.org/docs/maven-cxf-codegen-plugin-wsdl-to-java.html

Problems with XMLGregorianCalendar

The conversion between wsdl dataTypes and java dataTypes is held automatically by this plugin, so in most cases, you don´t need to worry about anything more.
But one annoying thing most people notes is the conversión between date/dateTime and XMLGregorianCalendar.

For example, by having this detifintion:
<xs:complexType name="operacionesPagoFiltroDTO">
 <xs:sequence>
  <xs:element minOccurs="0" name="entidad" type="xs:long" />
  <xs:element minOccurs="0" name="fechaDesde" type="xs:dateTime" />
  <xs:element minOccurs="0" name="fechaHasta" type="xs:dateTime" />
  <xs:element minOccurs="0" name="nifUsuario" type="xs:string" />
  <xs:element minOccurs="0" name="seccion" type="xs:long" />
 </xs:sequence>
</xs:complexType>

You will get this POJO:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "operacionesPagoFiltroDTO", propOrder = {
    "entidad",
    "fechaDesde",
    "fechaHasta",
    "nifUsuario",
    "seccion"
})
public class OperacionesPagoFiltroDTO {

    protected Long entidad;
    @XmlSchemaType(name = "dateTime")
    protected XMLGregorianCalendar fechaDesde;
    @XmlSchemaType(name = "dateTime")
    protected XMLGregorianCalendar fechaHasta;
    protected String nifUsuario;
    protected Long seccion;

    (...)

}

So, you have to manually make the conversion in order to obtain java.util.Date.

Solution

The solution is to use a custom binding file and a dateAdapter. When CXF plugin is executed, it will check the binding configuration and will generate the java.util.Date automatically.

In pom.xml you have to specify the custom binding file:
<plugin>
 <groupId>org.apache.cxf</groupId>
 <artifactId>cxf-codegen-plugin</artifactId>
 <version>${cxf.version}</version>
 <executions>
  <execution>
   <id>generate-sources</id>
   <phase>generate-sources</phase>
   <configuration>
    <sourceRoot>${project.build.directory}/generated/cxf</sourceRoot>
    <wsdlOptions>        
     <wsdlOption>
      <wsdl>${basedir}/src/main/resources/wsdl/gestionEconomica/GestionEconomica.wsdl</wsdl>       
      <bindingFiles>
       <bindingFile>${basedir}/src/main/resources/wsdl/gestionEconomica/GestionEconomicaBinding.xml</bindingFile>
      </bindingFiles>
     </wsdlOption>
    </wsdlOptions>
   </configuration>
   <goals>
    <goal>wsdl2java</goal>
   </goals>
  </execution>
 </executions>
</plugin>

And this would be the binding file:
<jaxws:bindings wsdlLocation="GestionEconomica.xsd.wsdl"
 xmlns:jaxws="http://java.sun.com/xml/ns/jaxws" xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">

 <jaxws:bindings node="wsdl:definitions/wsdl:types/xs:schema[@targetNamespace='http://mytargetspace.es/']">
  
  <jxb:globalBindings xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <jxb:javaType 
       name="java.util.Date" 
       xmlType="xs:dateTime"
    parseMethod="org.apache.cxf.xjc.runtime.DataTypeAdapter.parseDateTime"
    printMethod="org.apache.cxf.xjc.runtime.DataTypeAdapter.printDateTime" />
  </jxb:globalBindings>
 </jaxws:bindings>
</jaxws:bindings>

By doing so, you will get this POJO:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "operacionesPagoFiltroDTO", propOrder = {
    "entidad",
    "fechaDesde",
    "fechaHasta",
    "nifUsuario",
    "seccion"
})
public class OperacionesPagoFiltroDTO {

    protected Long entidad;
    @XmlElement(type = String.class)
    @XmlJavaTypeAdapter(Adapter1 .class)
    @XmlSchemaType(name = "dateTime")
    protected Date fechaDesde;
    @XmlElement(type = String.class)
    @XmlJavaTypeAdapter(Adapter1 .class)
    @XmlSchemaType(name = "dateTime")
    protected Date fechaHasta;
    protected String nifUsuario;
    protected Long seccion;

    (...)
}

You can specify any adapter you want, but CXF also provides you with a basic implementation: org.apache.cxf.xjc.runtime.DataTypeAdapter. In order to use it, you need to add this extra dependency to your pom.xml:
<dependency>
 <groupId>org.apache.cxf.xjc-utils</groupId>
 <artifactId>cxf-xjc-runtime</artifactId>
 <version>${cxf-xjc-runtime.version}</version>
</dependency>


Note: You can check more about wsdl2java and eclipse in one of my previous posts: http://trabajosdesisifo.blogspot.com.es/2015/04/eclipse-maven-configure-wsdl2java.html

miércoles, 24 de junio de 2015

Integrate JAX-WS in Spring Context

Creating a JAX-WS client with javax.xml.ws.Service is very simple, but if you are working with spring, then you would like to integrate it in Spring Context. In this way, you can get your client by using @Autowired.

The solution is to create a Factory Class responsible for instanciate the client.

Other common requirement is to read the service endpoing from a config file, so it could vary from enviroment.

Factory code:
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import es.sisifo.test.ws.webservices.UsuarioWebService;
import es.sisifo.test.ws.webservicesimpl.UsuarioService;



public class UsuariosWebServiceFactoryBean implements FactoryBean<UsuarioWebService> {

 private final String namespaceUri;
 private final String serviceName;
 private final String wsdlLocation;
 private final String serviceEndPoint;


 public UsuariosWebServiceFactoryBean(final String namespaceUri, final String serviceName, final String wsdlLocation, final String serviceEndPoint) {
  this.namespaceUri = namespaceUri;
  this.serviceName = serviceName;
  this.wsdlLocation = wsdlLocation;
  this.serviceEndPoint = serviceEndPoint;
 }


 @Override
 public UsuarioWebService getObject() throws Exception {
  final QName serviceQName = new QName(namespaceUri, serviceName);
  final Resource serviceWSDLResource =  new ClassPathResource(wsdlLocation);

  final UsuarioService service = new UsuarioService(serviceWSDLResource.getURL(), serviceQName);
  final UsuarioWebService port = service.getUsuarioWebServiceImplPort();

  final BindingProvider bindingProvider = (BindingProvider)port;
  bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, serviceEndPoint);

  return port;
 }


 @Override
 public Class<?> getObjectType() {
     return UsuarioWebService.class;
 }


 @Override
 public boolean isSingleton() {
  return true;
 }
}

To use this class, you only need to declare this factory as a normal bean, for example, in the application-context.xml:
<bean id="usuarioService" class="es.sisifo.test.usuarios.webservice.UsuariosWebServiceFactoryBean">   
 <constructor-arg name="namespaceUri"      value="${webService.usuarios.namespaceUri}" />
 <constructor-arg name="serviceName"       value="${webService.usuarios.serviceName}" />
 <constructor-arg name="wsdlLocation"      value="${webService.usuarios.wsdlDocumentLocation}" />
 <constructor-arg name="serviceEndPoint"   value="${webService.usuario.serviceEndPoint}" />
</bean>

And this would be the properties file:
webService.usuarios.namespaceUri     = http://webservicesimpl.ws.testc.es/
webService.usuarios.serviceName      = usuarioService
webService.usuarios.wsdlLocation     = /wsdl/UsuarioService.wsdl
webService.usuarios.serviceEndPoint  = http://localhost:8080/test-ws/servizos/UsuarioService

Note that the WSDL file is in the application classpath, i.e, in main/resources folder, inside wsdl directory.
You can check how to create all necesary classes for the client in this post: http://trabajosdesisifo.blogspot.com.es/2015/04/eclipse-maven-configure-wsdl2java.html 

sábado, 4 de octubre de 2014

WS integration in multimodule Maven Project

Objetivo:

  • Tener un módulo maven común (módulo api) con el modelo y la definición de un servicio web.
  • Tener un projecto maven de tipo WAR que implemente el servicio y lo publique como un web service.
  • Tener un proyecto maven de tipo WAR que consuma ese servicio web.




La forma habitual de trabajar con web services es disponer del modelo de clases en el servidor y generar una copia, por ejemplo con wsimport, en los distintos clientes. 
Con esto se tiene replicada la misma clase en cada cliente.

Lo ideal es tener esas clases en un único sitio, un jar, y que tanto el servidor como los clientes importen esa dependencia, sin tener que volver a crearla.

La ventaja de no replicar clases es que si el servidor publica dos fachadas de servicios, pero cuyos métodos comparten alguna clase de modelo en común, los clientes podrán hacer invocaciones entre ambos con la misma clase.


En este escenario, los dos WAR tendrán como dependencia común el módulo api 

Para la generación de los clientes se usará Spring WS, que únicamente necesita la dirección del WSDL e información de marshall/unmarshall para generar el cliente.
Esta informarción de marshalling también estará en el módulo api.


También hay un portlet que actúa como cliente de ese servicio web, y con spring-ws. La idea es no implementar lógica de negocio en los portlets y emplearlos únicamente como capa vista.

El código fuente de los proyectos está en:


El cliente WS proviene de la siguiente post de Krams:: Thanks for sharing!!!!

domingo, 1 de septiembre de 2013

Weblogic JAX-WS debug

Para mostrar los mensajes de request y response de los servicios web JAX hay que poner las siguientes opciones de JAVA.

 -Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true -Dcom.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump=true -Dcom.sun.xml.ws.transport.http.HttpAdapter.dump=true -Dcom.sun.xml.internal.ws.transport.http.HttpAdapter.dump=true  

Se modifica el fichero setDomainEnv.cmd que es el que controla el arranque del server. Por ejemplo seteándolo al final.

 set JAVA_OPTIONS=%JAVA_OPTIONS% -Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true -Dcom.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump=true -Dcom.sun.xml.ws.transport.http.HttpAdapter.dump=true -Dcom.sun.xml.internal.ws.transport.http.HttpAdapter.dump=true