cxf client code

	public static void main(String[] args) {
		JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
		factory.getInInterceptors().add(new LoggingInInterceptor());
		factory.getOutInterceptors().add(new LoggingOutInterceptor());
		factory.setServiceClass(es.depontevedra.soa.facturae.search.FacturaeSearch.class);
		factory.setAddress("http://HOST:PORT/serviciows/servicio?wsdl");
		HelloWorld client = (HelloWorld ) factory.create();
		 
		List<EstadoFactura> reply = client.setHi("Hola");
		System.out.println("Size: " + reply.size());
	}
cxf client code

CXF jaxb send string as CData

From http://cxf.547215.n5.nabble.com/CXF-jaxb-send-string-as-CData-td5524523.html
Finalmente no he necesitado usarlo, pero lo guardo por si algún día…

public class CdataWriterInterceptor extends AbstractPhaseInterceptor<Message> { 

        public CdataWriterInterceptor() { 
                super(Phase.PRE_STREAM); 
                addAfter(AttachmentOutInterceptor.class.getName()); 
        } 

        @Override 
        public void handleMessage(Message message) { 
                message.put("disable.outputstream.optimization", Boolean.TRUE); 
                XMLStreamWriter writer = 
StaxUtils.createXMLStreamWriter(message.getContent(OutputStream.class)); 
                message.setContent(XMLStreamWriter.class, new CDataXMLStreamWriter(writer)); 
        } 
} 
public class CDataXMLStreamWriter extends DelegatingXMLStreamWriter { 

        private String currentElementName; 

        public CDataXMLStreamWriter(XMLStreamWriter del) { 
                super(del); 
        } 

        @Override 
        public void writeCharacters(String text) throws XMLStreamException { 
                boolean useCData = checkIfCDATAneededForCurrentElement(); 
                if (useCData) { 
                        System.out.println("WritingCData" + text); 
                        super.writeCData(text); 
                }else { 
                super.writeCharacters(text); 
                } 
        } 

        private boolean checkIfCDATAneededForCurrentElement() { 
                if("MessageBody".equals(currentElementName)) return true; 
                return false; 
        } 

        public void writeStartElement(String prefix, String local, String 
uri) throws XMLStreamException { 
                currentElementName = local; 
                super.writeStartElement(prefix, local, uri); 
        } 
}
CXF jaxb send string as CData

Client Soap1.2 CXF

	<bean id="clientOtrasConsultasFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
		<property name="serviceClass" value="es.depontevedra.soap.cargows.services.OtrasConsultas" />
		<property name="address" value="#{url}otrasconsultasws" />
		<property name="bindingId" value="http://www.w3.org/2003/05/soap/bindings/HTTP/" />
	</bean>
	<bean id="clientOtrasConsultas" 
		class="es.depontevedra.soap.cargows.services.OtrasConsultas"
		factory-bean="clientOtrasConsultasFactory" factory-method="create" />
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:jaxws="http://cxf.apache.org/jaxws" 
	xmlns:soap="http://cxf.apache.org/bindings/soap" 
	xsi:schemaLocation="
	http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	http://cxf.apache.org/jaxws 
	http://cxf.apache.org/schemas/jaxws.xsd
	http://cxf.apache.org/bindings/soap
	http://cxf.apache.org/schemas/configuration/soap.xsd">

	<jaxws:client id="clientVR" address="http:/host:port/realizarVerificacion" 
		serviceClass="es.una.ruta.a.una.Interfaz"
		serviceName="VerificacionResidenciaFecha" >
		<jaxws:binding>
			<soap:soapBinding version="1.2"/>
		</jaxws:binding> 
	</jaxws:client>
Client Soap1.2 CXF

Diferencias Soap1.1 vs Soap 1.2

Sobre las peticiones que estoy haciendo en mis servicios:

---------------------------
ID: 1
Address: https://host:port/services/cargos.otrasconsultasws
Encoding: UTF-8
Content-Type: text/xml
Headers: {Accept=[*/*], Connection=[Keep-Alive], SOAPAction=["consultarMaestrosGenericos"]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body>...</soap:Body></soap:Envelope>
--------------------------------------
---------------------------
ID: 1
Address: https://host:port/services/cargos.otrasconsultasws
Encoding: UTF-8
Content-Type: application/soap+xml; action="consultarMaestrosGenericos"
Headers: {Accept=[*/*], Connection=[Keep-Alive]}
Payload: <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"><soap:Body>....</soap:Body></soap:Envelope>
--------------------------------------

DIFERENCIAS

Estas dos lineas son las mismas para ambos:
SOAP1.1 = SOAP1.2 = Address: https://elb.esb.wso2.com:8243/services/cargos.otrasconsultasws
SOAP1.1 = SOAP1.2 = Encoding: UTF-8

SOAP1.1 = Content-Type: text/xml
SOAP1.2 = Content-Type: application/soap+xml; action=»consultarMaestrosGenericos»

SOAP1.1 = Headers: {Accept=[*/*], Connection=[Keep-Alive], SOAPAction=[«consultarMaestrosGenericos»]}
SOAP1.2 = Headers: {Accept=[*/*], Connection=[Keep-Alive]}

SOAP1.1 = xmlns:soap=»http://schemas.xmlsoap.org/soap/envelope/»
SOAP1.2 = xmlns:soap=»http://www.w3.org/2003/05/soap-envelope»

Diferencias Soap1.1 vs Soap 1.2

JAXB y CXF

Recientemente me encuentro en proyectos donde por un lado tengo unos XSD que he transformado en clases y un servicio web que usa estas clases para comunicarse.

En mi modulo de generacion de clases, para poder realizar el marshal/unmarshal he usado spring con OXM. Mediante configuracion he indicado a Spring, donde estaban los XSD, para las validaciones y donde estaban las clases que tenia que tener en el contexto. Estos son los application context:

<?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:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="
		http://www.springframework.org/schema/util 
		http://www.springframework.org/schema/util/spring-util-3.0.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
        
    <import resource="classpath*:spring/spring-xsd-marshall.xml" />
    
	<util:constant id="m_jaxbFormattedOutput" static-field="javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT" />
	<util:constant id="m_jaxbEncoding" static-field="javax.xml.bind.Marshaller.JAXB_ENCODING" />

	<util:map id="marshallerPropertiesMap">
		<entry key-ref="m_jaxbEncoding" value="UTF-8" />
		<entry key-ref="m_jaxbFormattedOutput">
			<value type="java.lang.Boolean">true</value>
		</entry>
	</util:map>

	<bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller" >
		<property name="marshallerProperties" ref="marshallerPropertiesMap" />
		<property name="classesToBeBound" ref="classesToBeBoundList" />
		<property name="schemas" ref="schemasList" />
	</bean>
	
	<bean id="xmlB64Binder" class ="es.depontevedra.soa.xsd.spi.XmlB64Binder">
		<property name="encoding" value="UTF-8" />
	</bean>
	
	<bean id="xmlBinder" class ="es.depontevedra.soa.xsd.spi.XmlBinder">
		<property name="encoding" value="UTF-8" />
	</bean>

</beans>

Cada modulo en el que necesite serializar objetos, tiene que desarrollar su propio fichero de configuracion de serializacion para Spring:

<?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:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="
		http://www.springframework.org/schema/util 
		http://www.springframework.org/schema/util/spring-util-3.0.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    
<!-- ESTE ES EL FICHERO QUE CONTIENE LAS CLASES QUE REALIZARAN LA SERIALIZACION -->
<!-- <import resource="classpath*:spring/spring-xsd.xml" /> -->
<!-- EN ESTE FICHERO SOLO SE INDICA LO QUE SE QUIERE SERIALIZAR -->
    
    <util:list id="schemasList">
		<value>classpath:wsdl/confirmacion-peticion.xsd</value>
		<value>classpath:wsdl/datos-especificos.xsd</value>
		<value>classpath:wsdl/peticion.xsd</value>
		<value>classpath:wsdl/respuesta.xsd</value>
		<value>classpath:wsdl/soapfaultatributos.xsd</value>
		<value>classpath:wsdl/solicitud-respuesta.xsd</value>
	</util:list>
	
	<util:list id="classesToBeBoundList">
		<value>es.map.scsp.esquemas.datosespecificos.DatosDireccionType</value>
		<value>es.map.scsp.esquemas.datosespecificos.DatosEspecificos</value>
		<value>es.map.scsp.esquemas.datosespecificos.DatosNacimiento</value>
		<value>es.map.scsp.esquemas.datosespecificos.DatosNacimientoType</value>
		<value>es.map.scsp.esquemas.datosespecificos.DatosTitular</value>
		<value>es.map.scsp.esquemas.datosespecificos.EstadoResultado</value>
		<value>es.map.scsp.esquemas.datosespecificos.Organizacion</value>
		<value>es.map.scsp.esquemas.datosespecificos.SolicitanteDatos</value>
		<value>es.map.scsp.esquemas.datosespecificos.Solicitud</value>
		
		<value>es.map.scsp.esquemas.v2.confirmacionpeticion.Atributos</value>
		<value>es.map.scsp.esquemas.v2.confirmacionpeticion.ConfirmacionPeticion</value>
		<value>es.map.scsp.esquemas.v2.confirmacionpeticion.Estado</value>
		
		<value>es.map.scsp.esquemas.v2.peticion.Atributos</value>
		<value>es.map.scsp.esquemas.v2.peticion.DatosGenericos</value>
		<value>es.map.scsp.esquemas.v2.peticion.Emisor</value>
		<value>es.map.scsp.esquemas.v2.peticion.Estado</value>
		<value>es.map.scsp.esquemas.v2.peticion.Funcionario</value>
		<value>es.map.scsp.esquemas.v2.peticion.Peticion</value>
		<value>es.map.scsp.esquemas.v2.peticion.Solicitante</value>
		<value>es.map.scsp.esquemas.v2.peticion.Solicitudes</value>
		<value>es.map.scsp.esquemas.v2.peticion.SolicitudTransmision</value>
		<value>es.map.scsp.esquemas.v2.peticion.Titular</value>
		<value>es.map.scsp.esquemas.v2.peticion.Transmision</value>
		
		<value>es.map.scsp.esquemas.v2.respuesta.Atributos</value>
		<value>es.map.scsp.esquemas.v2.respuesta.DatosGenericos</value>
		<value>es.map.scsp.esquemas.v2.respuesta.Emisor</value>
		<value>es.map.scsp.esquemas.v2.respuesta.Estado</value>
		<value>es.map.scsp.esquemas.v2.respuesta.Funcionario</value>
		<value>es.map.scsp.esquemas.v2.respuesta.Respuesta</value>
		<value>es.map.scsp.esquemas.v2.respuesta.Solicitante</value>
		<value>es.map.scsp.esquemas.v2.respuesta.Titular</value>
		<value>es.map.scsp.esquemas.v2.respuesta.Transmision</value>
		<value>es.map.scsp.esquemas.v2.respuesta.TransmisionDatos</value>
		<value>es.map.scsp.esquemas.v2.respuesta.Transmisiones</value>
		
		<value>es.map.scsp.esquemas.v2.soapfaultatributos.Atributos</value>
		<value>es.map.scsp.esquemas.v2.soapfaultatributos.Estado</value>

		<value>es.map.scsp.esquemas.v2.solicitudrespuesta.Atributos</value>
		<value>es.map.scsp.esquemas.v2.solicitudrespuesta.Estado</value>
		<value>es.map.scsp.esquemas.v2.solicitudrespuesta.SolicitudRespuesta</value>
	</util:list>

</beans>

Tengo una clase en este modulo que es la encargada de realizar el Marshall/UnMarshall:

package es.depontevedra.soa.xsd.spi;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.springframework.beans.factory.annotation.Autowired;

import es.depontevedra.soa.xsd.XmlObjectBinder;

public abstract class MarshalUnmarshal implements XmlObjectBinder {

	private String encoding;

	@Autowired
	private org.springframework.oxm.jaxb.Jaxb2Marshaller jaxb2Marshaller;

	public MarshalUnmarshal() {
		super();
	}

	public String getEncoding() {
		return encoding;
	}

	public void setEncoding(String encoding) {
		this.encoding = encoding;
	}

	public Object xmlToObject(String xml) throws Exception {
		ByteArrayInputStream bais = new ByteArrayInputStream(xml.getBytes(getEncoding()));
		StreamSource source = new StreamSource(bais);
		return jaxb2Marshaller.unmarshal(source);
	}

	public String objectToXml(Object object) throws Exception {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		StreamResult result = new StreamResult(baos);
		jaxb2Marshaller.marshal(object, result);
		String xml = baos.toString(getEncoding()); 
		return xml;
	}

}
package es.depontevedra.soa.xsd.spi;

public class XmlBinder extends MarshalUnmarshal {

	public XmlBinder() {}
	
	public Object unmarshal(String xml) throws Exception {
		return xmlToObject(xml);
	}
	
	public String marshal(Object object) throws Exception {
		return objectToXml(object);
	}

}

Tambien esta la clase es.depontevedra.soa.xsd.spi.XmlB64Binder que hace lo mismo que XmlBinder pero los String son en BASE64.

Bien pues todo esto se puede hacer mucho mas sencillo:

	public String objectToXml(Object object) throws Exception {
		JAXBContext jc = JAXBContext.newInstance( "es.map.scsp.esquemas.v2.peticion:es.map.scsp.esquemas.datosespecificos:es.map.scsp.esquemas.v2.respuesta.Respuesta" );
		Marshaller marshaller = jc.createMarshaller();
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		marshaller.marshal(peticion, baos);
		
		return new String( baos.toByteArray(), "UTF-8" );
	}

	public Object xmlToObject(String xml) throws Exception {
		JAXBContext jc = JAXBContext.newInstance( "es.map.scsp.esquemas.v2.peticion:es.map.scsp.esquemas.datosespecificos:es.map.scsp.esquemas.v2.respuesta.Respuesta" );
		Unmarshaller unmarshaller = jc.createUnmarshaller();
		ByteArrayInputStream bais = new ByteArrayInputStream(xml.getBytes(getEncoding()));
		StreamSource source = new StreamSource(bais);
		return (Peticion) unmarshaller.unmarshal(source);
	}

Nota que los paquetes del contexto estan separados por dos puntos.

Habria que añadir mas codigo para las validaciones, generar clases, interfaces para las llamadas…
Cada vez que quieras realizar una accion puedes llamar a estos metodos de manera estatica, pasando por parametros los paquetes.

JAXB y CXF

Como modificar el mensaje SOAP que entra

Te llega un mensaje SOAP, y lo tienes que modificar porque hay una coma que esta en mal sitio. Vamos a usar Interceptors para ello.
Este es el mensaje que me llega y que quiero modificar:

<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <soapenv:Body>
                <soapenv:Fault>
                        <faultcode>env:Server</faultcode>
                        <faultstring>0301 - Organismo no autorizado 'XXXXXXXX' 'CDISFWS01'</faultstring>
                        <faultactor>CDISFWS01</faultactor>
                        <detail>
                                <Atributos xmlns="http://www.map.es/scsp/esquemas/V2/soapfaultatributos">
                                        <IdPeticion>1390410889105</IdPeticion>
                                        <NumElementos>1</NumElementos>
                                        <TimeStamp>2014-01-22T18:14:20.651+01:00</TimeStamp>
                                        <Estado>
                                                <CodigoEstado>0301</CodigoEstado>
                                                <CodigoEstadoSecundario />
                                                <LiteralError>Organismo no autorizado 'XXXXXXXX' 'CDISFWS01'</LiteralError>
                                                <TiempoEstimadoRespuesta>0</TiempoEstimadoRespuesta>
                                        </Estado>
                                        <CodigoCertificado>CDISFWS01</CodigoCertificado>
                                </Atributos>
                        </detail>
                </soapenv:Fault>
        </soapenv:Body>
</soapenv:Envelope>

Lo que esta mal:
env:Server
Como debiera esta bien:
soapenv:Server

<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <soapenv:Body>
                <soapenv:Fault>
                        <faultcode>soapenv:Server</faultcode>
                        <faultstring>0301 - Organismo no autorizado 'XXXXXXXX' 'CDISFWS01'</faultstring>
                        <faultactor>CDISFWS01</faultactor>
                        <detail>
                                <Atributos xmlns="http://www.map.es/scsp/esquemas/V2/soapfaultatributos">
                                        <IdPeticion>1390410889105</IdPeticion>
                                        <NumElementos>1</NumElementos>
                                        <TimeStamp>2014-01-22T18:14:20.651+01:00</TimeStamp>
                                        <Estado>
                                                <CodigoEstado>0301</CodigoEstado>
                                                <CodigoEstadoSecundario />
                                                <LiteralError>Organismo no autorizado 'XXXXXXXX' 'CDISFWS01'</LiteralError>
                                                <TiempoEstimadoRespuesta>0</TiempoEstimadoRespuesta>
                                        </Estado>
                                        <CodigoCertificado>CDISFWS01</CodigoCertificado>
                                </Atributos>
                        </detail>
                </soapenv:Fault>
        </soapenv:Body>
</soapenv:Envelope>

Utilizo esta clase que algun compañero de profesion dejo en stackoverflow.com. Yo simplemente he modificado el uso del log.

package una.ruta.a.un.paquete;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.commons.io.IOUtils;
import org.apache.cxf.binding.soap.interceptor.SoapPreProtocolOutInterceptor;
import org.apache.cxf.io.CachedOutputStream;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.slf4j.LoggerFactory;

public abstract class MessageChangeInterceptor extends AbstractPhaseInterceptor<Message> {

	private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(MessageChangeInterceptor.class);

	public MessageChangeInterceptor() {
		super(Phase.PRE_STREAM);
		addBefore(SoapPreProtocolOutInterceptor.class.getName());
	}

	protected abstract String changeOutboundMessage(String currentEnvelope);

	protected abstract String changeInboundMessage(String currentEnvelope);

	public void handleMessage(Message message) {
		boolean isOutbound = false;
		isOutbound = message == message.getExchange().getOutMessage()
				|| message == message.getExchange().getOutFaultMessage();

		if (isOutbound) {
			OutputStream os = message.getContent(OutputStream.class);

			CachedStream cs = new CachedStream();
			message.setContent(OutputStream.class, cs);

			message.getInterceptorChain().doIntercept(message);

			try {
				cs.flush();
				IOUtils.closeQuietly(cs);
				CachedOutputStream csnew = (CachedOutputStream) message.getContent(OutputStream.class);

				String currentEnvelopeMessage = IOUtils.toString(csnew.getInputStream(), "UTF-8");
				csnew.flush();
				IOUtils.closeQuietly(csnew);

				LOGGER.debug("Outbound message: " + currentEnvelopeMessage);

				String res = changeOutboundMessage(currentEnvelopeMessage);
				if (res != null) {
					LOGGER.debug("Outbound message has been changed: " + res);
				}
				res = res != null ? res : currentEnvelopeMessage;

				InputStream replaceInStream = IOUtils.toInputStream(res, "UTF-8");

				IOUtils.copy(replaceInStream, os);
				replaceInStream.close();
				IOUtils.closeQuietly(replaceInStream);

				os.flush();
				message.setContent(OutputStream.class, os);
				IOUtils.closeQuietly(os);

			} catch (IOException ioe) {
				LOGGER.warn("Unable to perform change.", ioe);
				throw new RuntimeException(ioe);
			}
		} else {
			try {
				InputStream is = message.getContent(InputStream.class);
				String currentEnvelopeMessage = IOUtils.toString(is, "UTF-8");
				IOUtils.closeQuietly(is);

				LOGGER.debug("Inbound message: " + currentEnvelopeMessage);

				String res = changeInboundMessage(currentEnvelopeMessage);
				if (res != null) {
					LOGGER.debug("Inbound message has been changed: " + res);
				}
				res = res != null ? res : currentEnvelopeMessage;

				is = IOUtils.toInputStream(res, "UTF-8");
				message.setContent(InputStream.class, is);
				IOUtils.closeQuietly(is);
			} catch (IOException ioe) {
				LOGGER.warn("Unable to perform change.", ioe);
				throw new RuntimeException(ioe);
			}
		}
	}

	public void handleFault(Message message) {
	}

	private class CachedStream extends CachedOutputStream {
		public CachedStream() {
			super();
		}

		protected void doFlush() throws IOException {
			currentStream.flush();
		}

		protected void doClose() throws IOException {
		}

		protected void onWrite() throws IOException {
		}
	}

}

Y la clase que hereda de la anterior, y realiza el cambio requerido:

package una.ruta.a.un.paquete;

import org.slf4j.LoggerFactory;

public class InterceptorInSoapFaultBug extends MessageChangeInterceptor {

	private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(InterceptorInSoapFaultBug.class);

	@Override
	protected String changeOutboundMessage(String currentEnvelope) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	protected String changeInboundMessage(String currentEnvelope) {
		if((currentEnvelope != null) && (currentEnvelope.contains(">env:Server<"))){
			LOGGER.info("Debido a un bug en el mensaje SoapFault de este servicio. se procede a cambiar la etiqueta "env:Server" por "soapenv:Server".");
			currentEnvelope = currentEnvelope.replace(">env:Server<", ">soapenv:Server<");
		}
		
		return currentEnvelope;
	}

}

Posteriormente en el fichero de configuracion de Spring tendrias que invocarlo:

	<!-- ... -->
	<bean id="bugSoapFault" class="una.ruta.a.un.paquete.InterceptorInSoapFaultBug" />
	<!-- ... -->
	<jaxws:client id="clientPaxaseConsultaIdentidad" address="#{url}"
		serviceClass="es.map.xml_schemas.PeticionPortType">
		<!-- <jaxws:properties> -->
		<!-- <entry key="schema-validation-enabled" value="true" /> -->
		<!-- </jaxws:properties> -->
		<jaxws:inFaultInterceptors>
			<ref bean="logInbound" />
			<ref bean="bugSoapFault" />
		</jaxws:inFaultInterceptors>
		<jaxws:inInterceptors>
	<!-- cierra todas las etiquetas y rellena lo que falta -->
Como modificar el mensaje SOAP que entra

javax.xml.soap.DetailEntry to xml

He encontrado documentacion de como crear los mensajes SoapFaul con el detalle como si fuese un XML. Pero ninguno que indique el procedimiento contrario. Tengo un SOAPFault con un xml en el detail y lo quiero serializar.

Este es el mensaje que me llega desde el WebService al que me conecto:

<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <soapenv:Body>
                <soapenv:Fault>
                        <faultcode>soapenv:Server</faultcode>
                        <faultstring>0301 - Organismo no autorizado 'P3600000H' 'CDISFWS01'</faultstring>
                        <faultactor>CDISFWS01</faultactor>
                        <detail>
                                <Atributos xmlns="http://www.map.es/scsp/esquemas/V2/soapfaultatributos">
                                        <IdPeticion>1390408700541</IdPeticion>
                                        <NumElementos>1</NumElementos>
                                        <TimeStamp>2014-01-22T17:37:49.601+01:00</TimeStamp>
                                        <Estado>
                                                <CodigoEstado>0301</CodigoEstado>
                                                <CodigoEstadoSecundario />
                                                <LiteralError>Organismo no autorizado 'P3600000H' 'CDISFWS01'</LiteralError>
                                                <TiempoEstimadoRespuesta>0</TiempoEstimadoRespuesta>
                                        </Estado>
                                        <CodigoCertificado>CDISFWS01</CodigoCertificado>
                                </Atributos>
                        </detail>
                </soapenv:Fault>
        </soapenv:Body>
</soapenv:Envelope>

Como lo trato:

	public String realizarConsulta(String datosPeticion) {
		try {
			// llamada al servicio, la parte, en la que va todo bien.
			// ...
		} catch (Exception e) {
			if(e instanceof javax.xml.ws.soap.SOAPFaultException){
				javax.xml.soap.SOAPFault fault = ((javax.xml.ws.soap.SOAPFaultException) e).getFault();
				es.map.scsp.esquemas.v2.soapfaultatributos.Atributos atributos = getSoapfaultatributo(fault);
				if(atributos != null){
					String codigoEstado = atributos.getEstado().getCodigoEstado();
					String literalError = atributos.getEstado().getLiteralError();
					return errorMesagge(codigoEstado, literalError);
				}else{
					String faultCode = fault.getFaultCode();
					String faultString = fault.getFaultString();
					return errorMesagge(faultCode, faultString);					
				}
			}else{
				LOGGER.error("ERROR", e);				
				return errorMesagge(null, e.getMessage());
			}
		}
	}

	@SuppressWarnings("unchecked")
	private es.map.scsp.esquemas.v2.soapfaultatributos.Atributos getSoapfaultatributo(javax.xml.soap.SOAPFault fault) {
		es.map.scsp.esquemas.v2.soapfaultatributos.Atributos atributos = null;
		try {
			javax.xml.soap.Detail detail = fault.getDetail();
			if(detail != null){
				Iterator<DetailEntry> iterator = detail.getDetailEntries();
				while(iterator.hasNext()){
					DetailEntry detailEntry = (DetailEntry) iterator.next();

					Document document = detailEntry.getOwnerDocument(); 
					DOMImplementationLS domImplLS = (DOMImplementationLS) document .getImplementation(); 
					LSSerializer serializer = domImplLS.createLSSerializer(); 
					String str = serializer.writeToString(detailEntry);
					str = str.replace("UTF-16", "UTF-8");
					LOGGER.trace("detailEntry: "+ str);
					
					Object xmlBinderObjeto = xmlBinder.unmarshal(str);

					if(xmlBinderObjeto instanceof es.map.scsp.esquemas.v2.soapfaultatributos.Atributos){
						atributos = (es.map.scsp.esquemas.v2.soapfaultatributos.Atributos) xmlBinderObjeto;
					}
				}
			}
		} catch (Exception e) {
			LOGGER.warn("ERROR al deserializar ATRIBUTOS del SOAPFault. ", e);
		}
		
		return atributos;
	}

Ahora bien, por ejemplo, no tienes el xsd del mensaje o te lo han cambiado si avisar…

	@SuppressWarnings("unchecked")
	@Override
	public String realizarConsulta(String datosPeticion) {
		try {
			// llamada al servicio, la parte, en la que va todo bien.
			// ...
		} catch (Exception e) {
			if(e instanceof javax.xml.ws.soap.SOAPFaultException){
				javax.xml.soap.SOAPFault fault = ((javax.xml.ws.soap.SOAPFaultException) e).getFault();
				String faultCode = fault.getFaultCode();
				String faultString = fault.getFaultString();
				LOGGER.info("faultCode: "+faultCode);
				LOGGER.info("faultString: "+faultString);
				
				javax.xml.soap.Detail detail = fault.getDetail();
				if(detail != null){
					Iterator<DetailEntry> iterator = detail.getDetailEntries();
					while(iterator.hasNext()){
						DetailEntry detailEntry = (DetailEntry) iterator.next();
						String NamespaceUri_soapfaultatributos = "http://www.map.es/scsp/esquemas/V2/soapfaultatributos";
						if(NamespaceUri_soapfaultatributos.equalsIgnoreCase(detailEntry.getElementQName().getNamespaceURI())){
							Iterator<?> it = detailEntry.getChildElements();
							String codigoEstado = null; 
							String literalError = null;
							String idPeticion = null;
							String timeStamp = null;
							while(it.hasNext()){
								Object object = it.next();
								if(object instanceof SOAPElement){
									SOAPElement element = (SOAPElement) object;
									String name = element.getLocalName();
									if("IdPeticion".equalsIgnoreCase(name)){
										idPeticion = element.getValue();
										LOGGER.info("IdPeticion: "+idPeticion);
									}else if("TimeStamp".equalsIgnoreCase(name)){
										timeStamp = element.getValue();
										LOGGER.info("TimeStamp: "+timeStamp);
									}else if("Estado".equalsIgnoreCase(name)){
										Iterator<?> itElements = element.getChildElements();
										while(itElements.hasNext()){
											Object objeto = itElements.next();
											if(objeto instanceof SOAPElement){
												SOAPElement elementHijo = (SOAPElement) objeto;
												String nameHijo = elementHijo.getLocalName();
												if("CodigoEstado".equalsIgnoreCase(nameHijo)){
													codigoEstado = elementHijo.getValue();
													LOGGER.info("CodigoEstado: "+codigoEstado);
												}else if("LiteralError".equalsIgnoreCase(nameHijo)){
													literalError = elementHijo.getValue();
													LOGGER.info("LiteralError: "+literalError);
												}
											}
										}
									}
								}
							}

						}

					}
				}

				return errorMesagge(faultCode, faultString);
			}else{
				LOGGER.error("ERROR", e);				
				return errorMesagge(null, e.getMessage());
			}
		}
	}

Ni decir falta que habria que refactorizar un poco 😉 jeje.

javax.xml.soap.DetailEntry to xml

CXF SoapFault service details error

Cuando sucede un error en un servicio web. Se devuelve un mensaje SoapFault. Indicando codigo y error. Pero no tenemos traza del error. Si quieres que tu servicio devuelva tambien la traza de error en el mensaje SoapFault debes indicarlo asi:

	<jaxws:endpoint address="/consultarIdentidad" id="mockConsulta" 
		wsdlLocation="wsdl/ConsultaIdentidad.wsdl"
		implementor="es.depontevedra.soap.interoperabilidad.identidad.services.paxase.ConsultaIdentidad">
		<jaxws:properties>
			<entry key="exceptionMessageCauseEnabled" value="true" />
			<entry key="faultStackTraceEnabled" value="true" />
		</jaxws:properties>
	</jaxws:endpoint>

faultStackTraceEnabled: es la parte stack trace de la excepcion.
exceptionMessageCauseEnabled: es la parte Caused by:…

Bibliografia

CXF SoapFault service details error

CXF no firmar, encriptar los SoapFault de respuesta

En cxf cuando firmamos, encriptamos… los mensajes SOAP, tanto cliente como servidor, deben de comprobar siempre que la cabecera esta firmada, o encriptada o … Pero que pasa cuando los mensajes SOAPFault no estan firmados, o encriptados o …

Pues que hay un error indicando: org.apache.cxf.binding.soap.SoapFault: No SIGNED element found matching XPath /soapenv:Envelope/soapenv:Body

Como indicar a CXF que no intente comprobar la seguridad de los mensajes que son SoapFault:

	<cxf:bus>
		<cxf:features>
			<cxf:logging />
		</cxf:features>
		<cxf:inInterceptors>
			<ref bean="checkResponse" />
			<bean class="org.apache.cxf.ws.security.wss4j.DefaultCryptoCoverageChecker">
            	<property name="checkFaults" value="false"/>
        	</bean>
		</cxf:inInterceptors>
		<cxf:outInterceptors>
			<ref bean="SignRequest" />
		</cxf:outInterceptors>
	</cxf:bus>

Jira en CXF informando y solucionando este bug.

CXF no firmar, encriptar los SoapFault de respuesta