web-services soap jax-ws paypal webservice-client

web services - Agregue el objeto de encabezado SOAP utilizando JAX-WS puro



web-services paypal (5)

Creé un método de exposición de servicios web con params usuario y contraseña como un encabezado como este:

@WebService(serviceName="authentication") public class WSAuthentication { String name = null; String password = null; public WSAuthentication() { super(); } public WSAuthentication(String name, String password) { this.name = name; this.password = password; } private static String getData(WSAuthentication sec) { System.out.println("********************* AUTHENTICATION ********************" + "/n" + "**********USER: " + sec.name + "/n" + "******PASSWORD: " + sec.password + "/n" + "******************************** AUTHENTICATION ****************************"); return sec.name + " -- " + sec.password; } @WebMethod(operationName="security", action="authenticate") @WebResult(name="answer") public String security(@WebParam(header=true, mode=Mode.IN, name="user") String user, @WebParam(header=true, mode=Mode.IN, name="password") String password) { WSAuthentication secure = new WSAuthentication(user, password); return getData(secure); } }

Intente compilarlo y las pruebas generadas a partir de la clase WSDL. Espero que esto ayude.

Estoy intentando implementar un cliente de servicio web simple para la API de PayPal Express Checkout utilizando JAX WS . PayPal Express Checkout API proporciona un archivo WSDL , desde el cual pude generar clases Java utilizando la utilidad wsdl2java de CXF .

Por razones de autenticación, exige agregar el encabezado SOAP a cada solicitud. Este encabezado es bastante simple y debería verse así: https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_ECSOAPAPIBasics#id09C3I0CF0O6

Las clases generadas a partir de WSDL incluyen ebay.apis.eblbasecomponents.CustomSecurityHeaderType class, que representa el encabezado que debo agregar a cada solicitud.

Entonces, la pregunta es: ¿cómo puedo agregar una instancia creada manualmente de la clase CustomSecurityHeaderType al encabezado de la solicitud SOAP teniendo en cuenta las siguientes condiciones:

  1. No estoy muy ansioso por utilizar las clases del paquete com.sun. * Como se menciona en la respuesta aquí: JAX-WS - Agregar encabezados SOAP (principalmente debido a posibles problemas de portabilidad entre diferentes JDK)
  2. No deseo ordenar manualmente ese objeto en instancias javax.xml.soap.SOAPElement anidadas como se menciona en la respuesta aquí: Cómo agrego un encabezado SOAP usando Java JAX-WS

Esta solución funciona muy bien, pero hay una trampa. Genera este error cuando se procesa el mensaje entrante:

dic 19, 2012 7:00:55 PM com.sun.xml.messaging.saaj.soap.impl.EnvelopeImpl addHeader SEVERE: SAAJ0120: no se puede agregar una cabecera si ya hay una Exception in thread "main" javax.xml.ws.WebServiceException: java.lang.RuntimeException: com.sun.xml.messaging.saaj.SOAPExceptionImpl: Can''t add a header when one is already present. at com.sun.xml.ws.handler.ClientSOAPHandlerTube.callHandlersOnResponse(ClientSOAPHandlerTube.java:167) at com.sun.xml.ws.handler.HandlerTube.processResponse(HandlerTube.java:174) at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:1074) at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:979) at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:950) at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:825) at com.sun.xml.ws.client.Stub.process(Stub.java:443) at com.sun.xml.ws.client.sei.SEIStub.doProcess(SEIStub.java:174) at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:119) at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:102) at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:154) at $Proxy38.wsRdyCrearTicketDA(Unknown Source) at ar.com.fit.fides.remedy.api.ws.ServicioCreacionTickets.crearTicket(ServicioCreacionTickets.java:55) at ar.com.fit.fides.remedy.api.ws.ConectorRemedyWS.crearTicket(ConectorRemedyWS.java:43) at ar.com.fit.fides.remedy.api.ws.ConectorRemedyWS.main(ConectorRemedyWS.java:90) Caused by: java.lang.RuntimeException: com.sun.xml.messaging.saaj.SOAPExceptionImpl: Can''t add a header when one is already present. at ar.com.fit.fides.remedy.api.ws.AuthenticationHandler.handleMessage(AuthenticationHandler.java:50) at ar.com.fit.fides.remedy.api.ws.AuthenticationHandler.handleMessage(AuthenticationHandler.java:23) at com.sun.xml.ws.handler.HandlerProcessor.callHandleMessageReverse(HandlerProcessor.java:341) at com.sun.xml.ws.handler.HandlerProcessor.callHandlersResponse(HandlerProcessor.java:214) at com.sun.xml.ws.handler.ClientSOAPHandlerTube.callHandlersOnResponse(ClientSOAPHandlerTube.java:161) ... 14 more Caused by: com.sun.xml.messaging.saaj.SOAPExceptionImpl: Can''t add a header when one is already present. at com.sun.xml.messaging.saaj.soap.impl.EnvelopeImpl.addHeader(EnvelopeImpl.java:128) at com.sun.xml.messaging.saaj.soap.impl.EnvelopeImpl.addHeader(EnvelopeImpl.java:108) at ar.com.fit.fides.remedy.api.ws.AuthenticationHandler.handleMessage(AuthenticationHandler.java:45)

Entonces, la solución es verificar si el mensaje se maneja si el mensaje saliente, como este:

public boolean handleMessage(SOAPMessageContext context) { try { Boolean outbound = (Boolean) context.get("javax.xml.ws.handler.message.outbound"); if (outbound != null && outbound) { // obtaining marshaller which should marshal instance to xml final Marshaller marshaller = JAXBContext.newInstance(AuthenticationInfo.class).createMarshaller(); // adding header because otherwise it''s null final SOAPHeader soapHeader = context.getMessage().getSOAPPart().getEnvelope().addHeader(); // marshalling instance (appending) to SOAP header''s xml node marshaller.marshal(info, soapHeader); } } catch (final Exception e) { throw new RuntimeException(e); } return true; }



Encontré esta respuesta:

JAX-WS - Agregar encabezados SOAP

Básicamente, usted agrega -XAdditionalHeaders a las opciones del compilador y los objetos en los encabezados también aparecen en el código generado como parámetros del método.


Por lo tanto, parece que he encontrado una posible respuesta al combinar JAX-WS y JAXB respuestas relacionadas de SO (realmente agradecería si alguien con experiencia en estas tecnologías puede verificar si el seguimiento es correcto):

Lo más obvio para mí es agregar el manejador de mensajes SOAP y alterar el encabezado de la instancia de SOAPMessage en él:

import javax.xml.ws.Binding; import javax.xml.ws.BindingProvider; import javax.xml.ws.handler.Handler; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.Marshaller; import javax.xml.soap.SOAPHeader; import ebay.api.paypalapi.ObjectFactory; // class generated by wsdl2java // following class is generated by wsdl2java utility Service class final PayPalAPIInterfaceService payPalService = new PayPalAPIInterfaceService(); final PayPalAPIAAInterface expressCheckoutPort = payPalService.getPayPalAPIAA(); final Binding binding = ((BindingProvider) expressCheckoutPort).getBinding(); List<Handler> handlersList = new ArrayList<Handler>(); // now, adding instance of Handler to handlersList which should do our job: // creating header instance final CustomSecurityHeaderType headerObj = new CustomSecurityHeaderType(); final UserIdPasswordType credentials = new UserIdPasswordType(); credentials.setUsername("username"); credentials.setPassword("password"); credentials.setSignature("signature"); headerObj.setCredentials(credentials); // bookmark #1 - please read explanation after code final ObjectFactory objectFactory = new ObjectFactory(); // creating JAXBElement from headerObj final JAXBElement<CustomSecurityHeaderType> requesterCredentials = objectFactory.createRequesterCredentials(headerObj); handlersList.add(new SOAPHandler<SOAPMessageContext>() { @Override public boolean handleMessage(final SOAPMessageContext context) { try { // checking whether handled message is outbound one as per Martin Strauss answer final Boolean outbound = (Boolean) context.get("javax.xml.ws.handler.message.outbound"); if (outbound != null && outbound) { // obtaining marshaller which should marshal instance to xml final Marshaller marshaller = JAXBContext.newInstance(CustomSecurityHeaderType.class).createMarshaller(); // adding header because otherwise it''s null final SOAPHeader soapHeader = context.getMessage().getSOAPPart().getEnvelope().addHeader(); // marshalling instance (appending) to SOAP header''s xml node marshaller.marshal(requesterCredentials, soapHeader); } } catch (final Exception e) { throw new RuntimeException(e); } return true; } // ... default implementations of other methods go here }); // as per Jean-Bernard Pellerin''s comment setting handlerChain list here, after all handlers were added to list binding.setHandlerChain(handlersList);

Explicación del marcador n.º 1 : no se debe ordenar el objeto del encabezado en sí, sino JAXBElement que representa ese objeto, porque de lo contrario se obtendrá una excepción. Se debe usar una de las clases de ObjectFactory que se generan a partir de WSDL para crear instancias de JAXBElement necesarias a partir de objetos originales. (Gracias @skaffman por la respuesta: No @XmlRootElement generado por JAXB )

También debería referirse a la respuesta de Martin Straus que extiende esta