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
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); } }
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>
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»
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.
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.
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.
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:…
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.