paso - soap web service java
Seguimiento de solicitudes/respuestas XML con JAX-WS (15)
¿Existe una manera fácil (también conocida como: no utilizar un proxy) de acceder al XML de solicitud / respuesta sin formato para un servicio web publicado con la implementación de referencia JAX-WS (la incluida en JDK 1.5 y mejor)? Ser capaz de hacer eso a través de un código es lo que debo hacer. Simplemente tenerlo conectado a un archivo mediante configuraciones inteligentes de inicio de sesión sería bueno, pero suficiente.
Sé que existen otros marcos más complejos y completos que podrían hacer eso, pero me gustaría mantenerlo lo más simple posible, y axis, cxf, etc. todos agregan una sobrecarga considerable que quiero evitar.
¡Gracias!
¿Estoy en lo correcto al entender que desea cambiar / acceder al mensaje XML sin formato?
Si es así, usted (o desde que tiene cinco años, el próximo) podría querer echarle un vistazo a la interfaz del proveedor que es parte de JAXWS. La contraparte del cliente se hace usando la clase "Despacho". De todos modos, no es necesario agregar manejadores o interceptores. Todavía puedes CAN, por supuesto. La desventaja es de esta manera, usted es COMPLETAMENTE responsable de construir SOAPMessage, pero es fácil, y si eso es lo que quiere (como yo lo hice), esto es perfecto.
Aquí hay un ejemplo para el lado del servidor (un poco torpe, fue solo para experimentar) -
@WebServiceProvider(portName="Provider1Port",serviceName="Provider1",targetNamespace = "http://localhost:8123/SoapContext/SoapPort1")
@ServiceMode(value=Service.Mode.MESSAGE)
public class Provider1 implements Provider<SOAPMessage>
{
public Provider1()
{
}
public SOAPMessage invoke(SOAPMessage request)
{ try{
File log= new File("/home/aneeshb/practiceinapachecxf/log.txt");//creates file object
FileWriter fw=new FileWriter(log);//creates filewriter and actually creates file on disk
fw.write("Provider has been invoked");
fw.write("This is the request"+request.getSOAPBody().getTextContent());
MessageFactory mf = MessageFactory.newInstance();
SOAPFactory sf = SOAPFactory.newInstance();
SOAPMessage response = mf.createMessage();
SOAPBody respBody = response.getSOAPBody();
Name bodyName = sf.createName("Provider1Insertedmainbody");
respBody.addBodyElement(bodyName);
SOAPElement respContent = respBody.addChildElement("provider1");
respContent.setValue("123.00");
response.saveChanges();
fw.write("This is the response"+response.getSOAPBody().getTextContent());
fw.close();
return response;}catch(Exception e){return request;}
}
}
Lo publica como lo haría con un SEI,
public class ServerJSFB {
protected ServerJSFB() throws Exception {
System.out.println("Starting Server");
System.out.println("Starting SoapService1");
Object implementor = new Provider1();//create implementor
String address = "http://localhost:8123/SoapContext/SoapPort1";
JaxWsServerFactoryBean svrFactory = new JaxWsServerFactoryBean();//create serverfactorybean
svrFactory.setAddress(address);
svrFactory.setServiceBean(implementor);
svrFactory.create();//create the server. equivalent to publishing the endpoint
System.out.println("Starting SoapService1");
}
public static void main(String args[]) throws Exception {
new ServerJSFB();
System.out.println("Server ready...");
Thread.sleep(10 * 60 * 1000);
System.out.println("Server exiting");
System.exit(0);
}
}
O puede usar una clase Endpoint para eso. Espero que haya sido útil.
Y, oh, si lo deseas, no necesitas ocuparte de los encabezados y demás, si cambias el modo de servicio a PAYLOAD (solo obtendrás el cuerpo de jabón).
// Esta solución proporciona una forma programática de agregar un controlador al servicio web clien sin la configuración XML
// Ver el documento completo aquí: http://docs.oracle.com/cd/E17904_01//web.1111/e13734/handlers.htm#i222476
// Crea una nueva clase que implementa SOAPHandler
public class LogMessageHandler implements SOAPHandler<SOAPMessageContext> {
@Override
public Set<QName> getHeaders() {
return Collections.EMPTY_SET;
}
@Override
public boolean handleMessage(SOAPMessageContext context) {
SOAPMessage msg = context.getMessage(); //Line 1
try {
msg.writeTo(System.out); //Line 3
} catch (Exception ex) {
Logger.getLogger(LogMessageHandler.class.getName()).log(Level.SEVERE, null, ex);
}
return true;
}
@Override
public boolean handleFault(SOAPMessageContext context) {
return true;
}
@Override
public void close(MessageContext context) {
}
}
// Programemente agregue su LogMessageHandler
com.csd.Service service = null;
URL url = new URL("https://service.demo.com/ResService.svc?wsdl");
service = new com.csd.Service(url);
com.csd.IService port = service.getBasicHttpBindingIService();
BindingProvider bindingProvider = (BindingProvider)port;
Binding binding = bindingProvider.getBinding();
List<Handler> handlerChain = binding.getHandlerChain();
handlerChain.add(new LogMessageHandler());
binding.setHandlerChain(handlerChain);
Actualmente. Si busca fuentes de HttpClientTransport notará que también está escribiendo mensajes en java.util.logging.Logger. Lo que significa que también puedes ver esos mensajes en tus registros.
Por ejemplo, si está utilizando Log4J2, todo lo que necesita hacer es lo siguiente:
- agrega el puente JUL-Log4J2 en tu camino de clase
- establecer el nivel TRACE para el paquete com.sun.xml.internal.ws.transport.http.client.
- agregue la propiedad del sistema -Djava.util.logging.manager = org.apache.logging.log4j.jul.LogManager a su línea de comando de inicio de applicaton
Después de estos pasos, comienza a ver mensajes SOAP en sus registros.
Antes de iniciar tomcat, configure JAVA_OPTS
como se muestra a continuación en envs de Linux. Luego, inicie Tomcat. Verá la solicitud y la respuesta en el archivo catalina.out
.
export JAVA_OPTS="$JAVA_OPTS -Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true"
Aquí está la solución en código sin procesar (reunidos gracias a stjohnroe y Shamik):
Endpoint ep = Endpoint.create(new WebserviceImpl());
List<Handler> handlerChain = ep.getBinding().getHandlerChain();
handlerChain.add(new SOAPLoggingHandler());
ep.getBinding().setHandlerChain(handlerChain);
ep.publish(publishURL);
Donde SOAPLoggingHandler está (arrancado de ejemplos vinculados):
package com.myfirm.util.logging.ws;
import java.io.PrintStream;
import java.util.Map;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
/*
* This simple SOAPHandler will output the contents of incoming
* and outgoing messages.
*/
public class SOAPLoggingHandler implements SOAPHandler<SOAPMessageContext> {
// change this to redirect output if desired
private static PrintStream out = System.out;
public Set<QName> getHeaders() {
return null;
}
public boolean handleMessage(SOAPMessageContext smc) {
logToSystemOut(smc);
return true;
}
public boolean handleFault(SOAPMessageContext smc) {
logToSystemOut(smc);
return true;
}
// nothing to clean up
public void close(MessageContext messageContext) {
}
/*
* Check the MESSAGE_OUTBOUND_PROPERTY in the context
* to see if this is an outgoing or incoming message.
* Write a brief message to the print stream and
* output the message. The writeTo() method can throw
* SOAPException or IOException
*/
private void logToSystemOut(SOAPMessageContext smc) {
Boolean outboundProperty = (Boolean)
smc.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
out.println("/nOutbound message:");
} else {
out.println("/nInbound message:");
}
SOAPMessage message = smc.getMessage();
try {
message.writeTo(out);
out.println(""); // just to add a newline
} catch (Exception e) {
out.println("Exception in handler: " + e);
}
}
}
Debe implementar un javax.xml.ws.handler.LogicalHandler, este manejador debe ser referenciado en un archivo de configuración del manejador, que a su vez es referenciado por una anotación @HandlerChain en su endpoint de servicio (interfaz o implementación). A continuación, puede enviar el mensaje a través de system.out o un registrador en la implementación de processMessage.
Ver
http://java.sun.com/mailers/techtips/enterprise/2006/TechTips_June06.html
En tiempo de ejecución , simplemente puedes ejecutar
com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump = true
como dump es una var pública definida en la clase de la siguiente manera
public static boolean dump;
Establezca las siguientes propiedades del sistema, esto habilitará el registro xml. Puede configurarlo en java o archivo de configuración.
static{
System.setProperty("com.sun.xml.ws.transport.http.client.HttpTransportPipe.dump", "true");
System.setProperty("com.sun.xml.ws.transport.http.HttpAdapter.dump", "true");
System.setProperty("com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump", "true");
System.setProperty("com.sun.xml.internal.ws.transport.http.HttpAdapter.dump", "true");
}
registros de la consola:
INFO: Outbound Message
---------------------------
ID: 1
Address: http://localhost:7001/arm-war/castService
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml
Headers: {Accept=[*/*], SOAPAction=[""]}
Payload: xml
--------------------------------------
INFO: Inbound Message
----------------------------
ID: 1
Response-Code: 200
Encoding: UTF-8
Content-Type: text/xml; charset=UTF-8
Headers: {content-type=[text/xml; charset=UTF-8], Date=[Fri, 20 Jan 2017 11:30:48 GMT], transfer-encoding=[chunked]}
Payload: xml
--------------------------------------
Estoy publicando una nueva respuesta, ya que no tengo la reputación suficiente para comentar sobre la proporcionada por (ver: share ).
En caso de que desee que el mensaje SOAP se imprima en un archivo (por ejemplo, a través de Log4j), puede usar:
OutputStream os = new ByteArrayOutputStream();
javax.xml.soap.SOAPMessage soapMsg = context.getMessage();
soapMsg.writeTo(os);
Logger LOG = Logger.getLogger(SOAPLoggingHandler.class); // Assuming SOAPLoggingHandler is the class name
LOG.info(os.toString());
Tenga en cuenta que, bajo ciertas circunstancias, el método llamado writeTo () puede no comportarse como se espera (ver: https://community.oracle.com/thread/1123104?tstart=0 o https://www.java.net/node/691073 ), por lo tanto, el siguiente código hará el truco:
javax.xml.soap.SOAPMessage soapMsg = context.getMessage();
com.sun.xml.ws.api.message.Message msg = new com.sun.xml.ws.message.saaj.SAAJMessage(soapMsg);
com.sun.xml.ws.api.message.Packet packet = new com.sun.xml.ws.api.message.Packet(msg);
Logger LOG = Logger.getLogger(SOAPLoggingHandler.class); // Assuming SOAPLoggingHandler is the class name
LOG.info(packet.toString());
Hay varias maneras de hacer esto programáticamente, como se describe en las otras respuestas, pero son mecanismos bastante invasivos. Sin embargo, si sabe que está utilizando JAX-WS RI (también conocido como "Metro"), puede hacerlo a nivel de configuración. Vea aquí para obtener instrucciones sobre cómo hacer esto. No es necesario que te metas con tu aplicación.
Inyectar SOAPHandler
en la interfaz del punto final. podemos rastrear la solicitud y respuesta SOAP
Implementando SOAPHandler con programación
ServerImplService service = new ServerImplService();
Server port = imgService.getServerImplPort();
/**********for tracing xml inbound and outbound******************************/
Binding binding = ((BindingProvider)port).getBinding();
List<Handler> handlerChain = binding.getHandlerChain();
handlerChain.add(new SOAPLoggingHandler());
binding.setHandlerChain(handlerChain);
Decorativamente agregando la @HandlerChain(file = "handlers.xml")
a su interfaz de punto final.
handlers.xml
<?xml version="1.0" encoding="UTF-8"?>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
<handler-chain>
<handler>
<handler-class>SOAPLoggingHandler</handler-class>
</handler>
</handler-chain>
</handler-chains>
SOAPLoggingHandler.java
/*
* This simple SOAPHandler will output the contents of incoming
* and outgoing messages.
*/
public class SOAPLoggingHandler implements SOAPHandler<SOAPMessageContext> {
public Set<QName> getHeaders() {
return null;
}
public boolean handleMessage(SOAPMessageContext context) {
Boolean isRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (isRequest) {
System.out.println("is Request");
} else {
System.out.println("is Response");
}
SOAPMessage message = context.getMessage();
try {
SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
SOAPHeader header = envelope.getHeader();
message.writeTo(System.out);
} catch (SOAPException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}
public boolean handleFault(SOAPMessageContext smc) {
return true;
}
// nothing to clean up
public void close(MessageContext messageContext) {
}
}
Las respuestas enumeradas aquí que lo guían para usar SOAPHandler
son totalmente correctas. El beneficio de este enfoque es que funcionará con cualquier implementación de JAX-WS, ya que SOAPHandler es parte de la especificación JAX-WS. Sin embargo, el problema con SOAPHandler es que implícitamente intenta representar todo el mensaje XML en la memoria. Esto puede llevar a un gran uso de memoria. Varias implementaciones de JAX-WS han agregado sus propias soluciones para esto. Si trabajas con solicitudes grandes o respuestas grandes, entonces debes considerar uno de los enfoques propios.
Como usted pregunta por "el que está incluido en JDK 1.5 o mejor", responderé con respecto a lo que formalmente se conoce como JAX-WS RI (también conocido como Metro), que es lo que se incluye con el JDK.
JAX-WS RI tiene una solución específica para esto que es muy eficiente en términos de uso de memoria.
Consulte https://javaee.github.io/metro/doc/user-guide/ch02.html#efficient-handlers-in-jax-ws-ri . Lamentablemente, ese enlace ahora está roto, pero puedes encontrarlo en WayBack Machine. Daré lo más destacado a continuación:
La gente de Metro en 2007 introduced un tipo de controlador adicional, MessageHandler<MessageHandlerContext>
, que es propiedad de Metro. Es mucho más eficiente que SOAPHandler<SOAPMessageContext>
ya que no intenta hacer una representación DOM en memoria.
Aquí está el texto crucial del artículo original del blog:
MessageHandler:
Utilizando el marco Handler extensible provisto por JAX-WS Specification y la mejor abstracción de mensajes en RI, presentamos un nuevo controlador llamado
MessageHandler
para extender sus aplicaciones de servicio web. MessageHandler es similar a SOAPHandler, excepto que las implementaciones de este tienen acceso aMessageHandlerContext
(una extensión de MessageContext). A través de MessageHandlerContext se puede acceder al Mensaje y procesarlo usando Message API. Al poner el título del blog, este controlador le permite trabajar en Message, que proporciona formas eficientes de acceder / procesar el mensaje, no solo un mensaje basado en DOM. El modelo de programación de los manejadores es el mismo y los manejadores de mensajes se pueden mezclar con manejadores lógicos y SOAP estándar. He agregado una muestra en JAX-WS RI 2.1.3 que muestra el uso de MessageHandler para registrar mensajes y aquí hay un fragmento de la muestra:
public class LoggingHandler implements MessageHandler<MessageHandlerContext> {
public boolean handleMessage(MessageHandlerContext mhc) {
Message m = mhc.getMessage().copy();
XMLStreamWriter writer = XMLStreamWriterFactory.create(System.out);
try {
m.writeTo(writer);
} catch (XMLStreamException e) {
e.printStackTrace();
return false;
}
return true;
}
public boolean handleFault(MessageHandlerContext mhc) {
.....
return true;
}
public void close(MessageContext messageContext) { }
public Set getHeaders() {
return null;
}
}
(Fin de la cita del blog de 2007)
No hace falta decir que su Handler, LoggingHandler
en el ejemplo, debe agregarse a la Cadena de su Handler para tener algún efecto. Esto es lo mismo que agregar cualquier otro Handler
, por lo que puede buscar en las otras respuestas de esta página cómo hacerlo.
Puede encontrar un ejemplo completo en el repositorio de Metro GitHub .
Las siguientes opciones habilitan el registro de todas las comunicaciones a la consola (técnicamente, solo necesita uno de estos, pero eso depende de las bibliotecas que use, por lo que configurar los cuatro es una opción más segura). Puede configurarlo en el código como en el ejemplo, o como parámetro de línea de comando utilizando -D o como variable de entorno como escribió Upendra.
System.setProperty("com.sun.xml.ws.transport.http.client.HttpTransportPipe.dump", "true");
System.setProperty("com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump", "true");
System.setProperty("com.sun.xml.ws.transport.http.HttpAdapter.dump", "true");
System.setProperty("com.sun.xml.internal.ws.transport.http.HttpAdapter.dump", "true");
Consulte la pregunta Seguimiento de solicitud / respuestas XML con JAX-WS cuando se produce un error para obtener más información.
Podría tratar de poner un ServletFilter
frente al servicio web e inspeccionar la solicitud y la respuesta que van a / devueltas del servicio.
Aunque específicamente no solicitó un proxy, a veces me parece que tcptrace es suficiente para ver qué sucede en una conexión. Es una herramienta simple, sin instalación, muestra los flujos de datos y también puede escribir en el archivo.
Una forma de hacerlo es no usar su código, sino usar analizadores de paquetes de red como Etheral o WireShark, que pueden capturar el paquete HTTP con el mensaje XML como carga útil y puede seguir registrando en un archivo más o menos.
Pero un enfoque más sofisticado es escribir sus propios manejadores de mensajes. Puedes echarle un vistazo http://java.sun.com/mailers/techtips/enterprise/2006/TechTips_June06.html .