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

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

CXF configurar bus cliente, servidor en un mismo servicio

Tengo un servicio que hace de proxy. La peticion que le llega la envia a otro Servicio Web y este otro devuelve la respuesta.

Por un lado tengo un modulo: cliente-servicio_externo.
Por otro lado tengo el modulo: servidor-proxy.

El cliente es una dependencia del servidor-proxy, para que mediante la configuracion de Spring, llamar al servicio externo.

	<cxf:bus bus="clientPaxaseBus">
		<cxf:features>
			<cxf:logging />
		</cxf:features>
		<cxf:inInterceptors>
			<ref bean="checkResponse" />
			<bean class="org.apache.cxf.ws.security.wss4j.DefaultCryptoCoverageChecker" />
		</cxf:inInterceptors>
		<cxf:outInterceptors>
			<ref bean="SignRequest" />
		</cxf:outInterceptors>
	</cxf:bus>
		
	<jaxws:client id="clientPaxaseConsultaIdentidad" address="#{url}/consultarIdentidad" bus="clientPaxaseBus"
		serviceClass="es.map.xml_schemas.PeticionPortType"/>

Pongo una configuracion de BUS generica, para todos los clientes. Especifico el nombre del bus: bus=”clientPaxaseBus”.

	<bean id="logInbound" class="org.apache.cxf.interceptor.LoggingInInterceptor" />
	<bean id="logOutbound" class="org.apache.cxf.interceptor.LoggingOutInterceptor" />

	<bean id="cdi" class="es.depontevedra.soap.interoperabilidad.identidad.services.spi.ConsultaIdentidadEnPaxase" />
	<jaxws:endpoint id="cdiService" implementor="#cdi" address="/consultaidentidad"
		publishedEndpointUrl="http://${service.host}:${service.port}/${service.wsname}/consultaidentidad">
		<jaxws:inFaultInterceptors>
			<ref bean="logInbound" />
		</jaxws:inFaultInterceptors>
		<jaxws:inInterceptors>
			<ref bean="logInbound" />
		</jaxws:inInterceptors>
		<jaxws:outFaultInterceptors>
			<ref bean="logOutbound" />
		</jaxws:outFaultInterceptors>
		<jaxws:outInterceptors>
			<ref bean="logOutbound" />
		</jaxws:outInterceptors>
	</jaxws:endpoint>

Evito la configuracion bus, poniendo los interceptors a mano en cada endpoint. No he sido capaz de generar un cxf:bus generico nombrando o etiquetandolo de alguna manera. Esta solucion no me gusta, pero funciona. A ver si encuentro una manera mas limpia…

CXF configurar bus cliente, servidor en un mismo servicio