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:
- 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)
- 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;
}
Si está utilizando maven, y jaxws-maven-plugin, todo lo que tiene que hacer es agregar el distintivo xadditionalHeaders a true y el cliente se generará con los métodos que tienen los encabezados como entrada.
https://jax-ws-commons.java.net/jaxws-maven-plugin/wsimport-mojo.html#xadditionalHeaders
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 )