He encontrado un BUG (CXF-5527) en Apache CXF

Me hace ilusion, la verdad.

Realmente el que lo reporta soy yo. https://issues.apache.org/jira/browse/CXF-5527
En el foro.

Ha salido publicado en la version 2.7.9. Aqui su Release Notes.

Anuncios
He encontrado un BUG (CXF-5527) en Apache CXF

No perder la conexion RMI

<?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:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" 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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/aop  
		http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

	<context:annotation-config />
	<context:component-scan base-package="es.depontevedra.cividas.rmi.connect" />

	<bean id="cividasE" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
		<property name="serviceUrl" value="rmi://${cividas.server.host}:${cividas.server.port}/${cividas.server.name}" />
		<property name="serviceInterface" value="com.ontimize.locator.EntityReferenceLocator" />
		<property name="refreshStubOnConnectFailure" value="true" />
	</bean>
	
	<bean id="cividasU" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
		<property name="serviceUrl" value="rmi://${cividas.server.host}:${cividas.server.port}/${cividas.server.name}" />
		<property name="serviceInterface" value="com.ontimize.locator.UtilReferenceLocator" />
		<property name="refreshStubOnConnectFailure" value="true" />
	</bean>
	
	<bean id="cividasRemoteEntity" class="es.depontevedra.cividas.rmi.connect.CividasRemoteEntity">
		<property name="user" value="${cividas.server.user}" />
		<property name="pass" value="${cividas.server.pass}" />
		<property name="host" value="${cividas.server.host}" />
		<property name="port" value="${cividas.server.port}" />
		<property name="name" value="${cividas.server.name}" />
	</bean>

	<bean id="cividasEntity" class="es.depontevedra.cividas.rmi.connect.CividasEntity">
		<property name="user" value="${cividas.server.user}" />
		<property name="pass" value="${cividas.server.pass}" />
		<property name="host" value="${cividas.server.host}" />
		<property name="port" value="${cividas.server.port}" />
		<property name="name" value="${cividas.server.name}" />
	</bean>

</beans>

depues en la clase que implemntes capturas:

public class CividasConnection {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(CividasConnection.class);
	
	@Autowired
	private EntityReferenceLocator cividas;
	
	@Autowired
	private UtilReferenceLocator cividasUtil;

No perder la conexion RMI

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

Como acceder a la pila de llamadas de la JVM

Post original de Emerson Miranda

Lo copio y pego, para no tardar en buscarlo

	public static void printStackTrace() {
		System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
		StackTraceElement[] stack = Thread.currentThread().getStackTrace();
		for(int pos=stack.length - 1; pos > 1; pos--){
			StackTraceElement elem = stack[pos];
			//se elimina el paquete del nombre de la clase
			String name = elem.getClassName().substring(elem.getClassName().lastIndexOf(".") + 1 );
			System.out.println(name + "." + elem.getMethodName() + ":"  + elem.getLineNumber());
			if(pos > 2)System.out.print("->");
		}
		System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
	}
Como acceder a la pila de llamadas de la JVM