2012-06-14 10 views
11

Mi sono divertito con gli intercettori lato server su CXF. Ma sembra che non sia un compito banale implementare semplici intercettori in entrata e in uscita che mi forniscano una stringa semplice contenente l'XML SOAP.Come ottenere xml sapone in entrata e in uscita in modo semplice con Apache CXF?

Ho bisogno di avere l'XML semplice nell'intercettore in modo che possa usarli per attività di registrazione specifiche. Gli intercettori LogIn & LogOut standard non sono all'altezza dell'attività. Qualcuno è disposto a condividere qualche esempio su come implementare un semplice intercettore in entrata che sia in grado di ottenere l'XML SOAP in arrivo e un intercettore in uscita per ottenere nuovamente l'XML SOAP?

risposta

13

trovato il codice per un intercettore in entrata qui: Logging request/response with Apache CXF as XML

mio intercettore in uscita:

import java.io.OutputStream; 

import org.apache.cxf.interceptor.Fault; 
import org.apache.cxf.interceptor.LoggingOutInterceptor; 
import org.apache.cxf.io.CacheAndWriteOutputStream; 
import org.apache.cxf.io.CachedOutputStream; 
import org.apache.cxf.io.CachedOutputStreamCallback; 
import org.apache.cxf.message.Message; 
import org.apache.cxf.phase.Phase; 

public class MyLogInterceptor extends LoggingOutInterceptor { 

    public MyLogInterceptor() { 
     super(Phase.PRE_STREAM); 
    } 

    @Override 
    public void handleMessage(Message message) throws Fault { 
     OutputStream out = message.getContent(OutputStream.class); 
     final CacheAndWriteOutputStream newOut = new CacheAndWriteOutputStream(out); 
     message.setContent(OutputStream.class, newOut); 
     newOut.registerCallback(new LoggingCallback()); 
    } 

    public class LoggingCallback implements CachedOutputStreamCallback { 
     public void onFlush(CachedOutputStream cos) { 
     } 

     public void onClose(CachedOutputStream cos) { 
      try { 
       StringBuilder builder = new StringBuilder(); 
       cos.writeCacheTo(builder, limit); 
       // here comes my xml: 
       String soapXml = builder.toString(); 
      } catch (Exception e) { 
      } 
     } 
    } 
} 
+1

Qual è il vantaggio del callback? – Basil

+1

Il metodo onClose() nel callback viene richiamato dopo che il flusso di output è stato svuotato e i suoi dati sono disponibili per il recupero. – annkatrin

+1

Cosa significa 'limite' in questo esempio? Non è dichiarato da nessuna parte ... – Cleankod

6

non ho potuto ottenere la soluzione di cui sopra a lavorare per me. Questo è quello che ho sviluppato e spero che possa aiutare gli altri:

mio intercettore "in entrata":

import org.apache.cxf.interceptor.LoggingInInterceptor; 
import org.apache.cxf.interceptor.LoggingMessage; 

public class MyCxfSoapInInterceptor extends LoggingInInterceptor { 


    public MyCxfSoapInInterceptor() { 
     super(); 
    } 

    @Override 
    protected String formatLoggingMessage(LoggingMessage loggingMessage) { 
     String soapXmlPayload = loggingMessage.getPayload() != null ? loggingMessage.getPayload().toString() : null; 

     // do what you want with the payload... in my case, I stuck it in a JMS Queue 

     return super.formatLoggingMessage(loggingMessage); 
    } 
} 

mio intercettore "in uscita":

import org.apache.cxf.interceptor.LoggingMessage; 
import org.apache.cxf.interceptor.LoggingOutInterceptor; 

public class MyCxfSoapOutInterceptor extends LoggingOutInterceptor { 

    public MyCxfSoapOutInterceptor() { 
     super(); 
    } 

    @Override 
    protected String formatLoggingMessage(LoggingMessage loggingMessage) { 
     String soapXmlPayload = loggingMessage.getPayload() != null ? loggingMessage.getPayload().toString() : null; 

     // do what you want with the payload... in my case, I stuck it in a JMS Queue 

     return super.formatLoggingMessage(loggingMessage); 
    } 
} 

Qualcosa ho aggiunto alla mia richiesta quadro di primavera contesto XML (ricordati di definire i due intercettori anche nel file XML) ...

... 

    <cxf:bus> 
     <cxf:inInterceptors> 
      <ref bean="myCxfSoapInInterceptor"/> 
     </cxf:inInterceptors> 
     <cxf:inFaultInterceptors> 
      <ref bean="myCxfSoapInInterceptor"/> 
     </cxf:inFaultInterceptors> 
     <cxf:outInterceptors> 
      <ref bean="myCxfSoapOutInterceptor"/> 
     </cxf:outInterceptors> 
     <cxf:outFaultInterceptors> 
      <ref bean="myCxfSoapOutInterceptor"/> 
     </cxf:outFaultInterceptors> 
    </cxf:bus> 

    ... 

Nota, ci sono altri modi per aggiungere gli intercettori come le annotazioni che ti permetteranno di intercettare solo servizi di sapone specifici. Il suddetto modo di aggiungere interceptor il "bus" intercetterebbe tutti i tuoi servizi di sapone.

+0

Ho fatto esattamente ciò che suggerisci ma il mio 'loggingOutInterceptor' non è stato attraversato. Per 'loggingInInterceptor' funziona correttamente come previsto. Sai perché? Grazie. – bryannguyen

4

Voglio solo condividere un'altra opzione, come ottenere contemporaneamente i messaggi in entrata e in uscita contemporaneamente per alcuni scopi di registrazione, ad esempio richieste di registro e risposte corrispondenti al database.

import javax.xml.namespace.QName; 
import javax.xml.soap.SOAPMessage; 
import javax.xml.soap.SOAPPart; 
import javax.xml.transform.TransformerException; 
import javax.xml.transform.TransformerFactory; 
import javax.xml.transform.dom.DOMSource; 
import javax.xml.transform.stream.StreamResult; 
import javax.xml.ws.handler.MessageContext; 
import javax.xml.ws.handler.soap.SOAPHandler; 
import javax.xml.ws.handler.soap.SOAPMessageContext; 
import java.io.StringWriter; 
import java.util.Collections; 
import java.util.Set; 

public class CxfLoggingHandler implements SOAPHandler<SOAPMessageContext> { 

private static final String SOAP_REQUEST_MSG_KEY = "REQ_MSG"; 

public Set<QName> getHeaders() { 
    return Collections.EMPTY_SET; 
} 

public boolean handleMessage(SOAPMessageContext context) { 
    Boolean outgoingMessage = (Boolean) context.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY); 
    if (outgoingMessage) { 
     // it is outgoing message. let's work 
     SOAPPart request = (SOAPPart)context.get(SOAP_REQUEST_MSG_KEY); 
     String requestString = convertDomToString(request); 
     String responseString = convertDomToString(context.getMessage().getSOAPPart()); 
     String soapActionURI = ((QName)context.get(MessageContext.WSDL_OPERATION)).getLocalPart(); 
     // now you can output your request, response, and ws-operation  
    } else { 
     // it is incoming message, saving it for future 
     context.put(SOAP_REQUEST_MSG_KEY, context.getMessage().getSOAPPart()); 
    } 
    return true; 
} 

public boolean handleFault(SOAPMessageContext context) {   
    return handleMessage(context); 
} 

private String convertDomToString(SOAPPart soap){ 
    final StringWriter sw = new StringWriter(); 
    try { 
     TransformerFactory.newInstance().newTransformer().transform(
       new DOMSource(soap), 
       new StreamResult(sw)); 
    } catch (TransformerException e) { 
     // do something 
    } 
    return sw.toString(); 
} 
} 

e quindi collegare tale gestore con webservice

<jaxws:endpoint id="wsEndpoint" implementor="#myWS" address="/myWS" > 
    <jaxws:handlers> 
     <bean class="com.package.handlers.CxfLoggingHandler"/> 
    </jaxws:handlers> 
</jaxws:endpoint> 
+0

Funziona perfettamente se si sostituisce 'if (outgoingMessage) {' con 'if (!outgoingMessage) {' – Ivano85

+0

Hm, dovrebbe essere" if (outgoingMessage) ". Poiché il messaggio in arrivo deve essere salvato per il futuro e quando abbiamo messaggio in uscita, siamo in grado di registrare i dati, dato che abbiamo accesso a entrambi: messaggi in entrata e in uscita ora. – error1009

+0

Siamo spiacenti, hai ragione perché questo è un endpoint: P ... L'ho usato lato client, quindi il messaggio in uscita era il più richiesto nel mio caso e il messaggio in arrivo la risposta. L'ho apprezzato molto, ottima soluzione ;-) – Ivano85

3

Esempio di scrittura del testo a uno StringBuffer, con ganci per la cattura di alcune proprietà personalizzate e il filtraggio della richiesta XML:

public class XMLLoggingInInterceptor extends AbstractPhaseInterceptor<Message> { 

    private static final String LOCAL_NAME = "MessageID"; 

    private static final int PROPERTIES_SIZE = 128; 

    private String name = "<interceptor name not set>"; 

    protected PrettyPrinter prettyPrinter = null; 
    protected Logger logger; 
    protected Level reformatSuccessLevel; 
    protected Level reformatFailureLevel; 

    public XMLLoggingInInterceptor() { 
     this(LogUtils.getLogger(XMLLoggingInInterceptor.class), Level.INFO, Level.WARNING); 
    } 

    public XMLLoggingInInterceptor(PrettyPrinter prettyPrinter) { 
     this(LogUtils.getLogger(XMLLoggingInInterceptor.class), Level.INFO, Level.WARNING); 

     this.prettyPrinter = prettyPrinter; 
    } 

    public XMLLoggingInInterceptor(Logger logger, Level reformatSuccessLevel, Level reformatFailureLevel) { 
     super(Phase.RECEIVE); 
     this.logger = logger; 
     this.reformatSuccessLevel = reformatSuccessLevel; 
     this.reformatFailureLevel = reformatFailureLevel; 
    } 

    public XMLLoggingInInterceptor(PrettyPrinter prettyPrinter, Logger logger, Level reformatSuccessLevel, Level reformatFailureLevel) { 
     this(logger, reformatSuccessLevel, reformatFailureLevel); 
     this.prettyPrinter = prettyPrinter; 
     this.logger = logger; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public void handleMessage(Message message) throws Fault { 

     if (!logger.isLoggable(reformatSuccessLevel)) { 
      return; 
     } 

     InputStream in = message.getContent(InputStream.class); 
     if (in == null) { 
      return; 
     } 

     StringBuilder buffer; 

     CachedOutputStream cache = new CachedOutputStream(); 
     try { 
      InputStream origIn = in; 
      IOUtils.copy(in, cache); 

      if (cache.size() > 0) { 
       in = cache.getInputStream(); 
      } else { 
       in = new ByteArrayInputStream(new byte[0]); 
      } 

      // set the inputstream back as message payload 
      message.setContent(InputStream.class, in); 

      cache.close(); 
      origIn.close(); 

      int contentSize = (int) cache.size(); 

      buffer = new StringBuilder(contentSize + PROPERTIES_SIZE); 

      cache.writeCacheTo(buffer, "UTF-8"); 
     } catch (IOException e) { 
      throw new Fault(e); 
     } 

     // decode chars from bytes 
     char[] chars = new char[buffer.length()]; 
     buffer.getChars(0, chars.length, chars, 0); 

     // reuse buffer 
     buffer.setLength(0); 

     // perform local logging - to the buffer 
     buffer.append(name); 

     logProperties(buffer, message); 

     // pretty print XML 
     if(prettyPrinter.process(chars, 0, chars.length, buffer)) { 
      // log as normal 
      logger.log(reformatSuccessLevel, buffer.toString()); 
     } else { 
      // something unexpected - log as exception 
      buffer.append(" was unable to format XML:\n"); 
      buffer.append(chars); // unmodified XML 

      logger.log(reformatFailureLevel, buffer.toString()); 
     } 
    } 


    /** 
    * Gets theMessageID header in the list of headers. 
    * 
    */ 
    protected String getIdHeader(Message message) { 
     return getHeader(message, LOCAL_NAME); 
    } 

    protected String getHeader(Message message, String name) { 
     List<Header> headers = (List<Header>) message.get(Header.HEADER_LIST); 

     if(headers != null) { 
      for(Header header:headers) { 
       if(header.getName().getLocalPart().equalsIgnoreCase(name)) { 
        return header.getObject().toString(); 
       } 
      } 
     } 
     return null; 
    }   

    /** 
    * Method intended for use within subclasses. Log custom field here. 
    * 
    * @param message message 
    */ 

    protected void logProperties(StringBuilder buffer, Message message) { 
     final String messageId = getIdHeader(message); 
     if(messageId != null) { 
      buffer.append(" MessageId="); 
      buffer.append(messageId); 
     } 
    } 

    public void setPrettyPrinter(PrettyPrinter prettyPrinter) { 
     this.prettyPrinter = prettyPrinter; 
    } 

    public PrettyPrinter getPrettyPrinter() { 
     return prettyPrinter; 
    } 

    public Logger getLogger() { 
     return logger; 
    } 

    public String getName() { 
     return name; 
    } 

    public Level getReformatFailureLevel() { 
     return reformatFailureLevel; 
    } 

    public Level getReformatSuccessLevel() { 
     return reformatSuccessLevel; 
    } 

    public void setReformatFailureLevel(Level reformatFailureLevel) { 
     this.reformatFailureLevel = reformatFailureLevel; 
    } 

    public void setReformatSuccessLevel(Level reformatSuccessLevel) { 
     this.reformatSuccessLevel = reformatSuccessLevel; 
    } 

    public void setLogger(Logger logger) { 
     this.logger = logger; 
    } 
} 

Per un esempio completamente funzionante, con gli intercettori di uscita, vedere il mio CXF module su github.

Problemi correlati