2012-06-14 14 views
11

Przechwalam się przechwytywaczami po stronie serwera na CXF. Ale wydaje się, że nie jest prostym zadaniem zaimplementowanie prostych przechwytywaczy przychodzących i wychodzących, które dają mi zwykły ciąg znaków zawierający XML SOAP.Jak uzyskać przychodzące i wychodzące mydło xml w prosty sposób za pomocą Apache CXF?

Potrzebuję mieć zwykły kod XML w przechwytywaczu, aby móc go używać do określonych zadań logowania. Standardowe przechwytywacze LogIn & LogOut nie nadają się do tego zadania. Czy ktoś chce podzielić się przykładem na to, w jaki sposób mógłbym wdrożyć prosty przechwytujący przechwytywacz, który byłby w stanie uzyskać przychodzący XML SOAP i wychodzący przechwytujący, aby ponownie uzyskać kod XML SOAP?

Odpowiedz

13

Znaleziony kod przychodzącego kolektora tutaj: Logging request/response with Apache CXF as XML

Moja towarzyski przechwytujących:

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

Jakie są zalety wywołania zwrotnego? – Basil

+1

Metoda onClose() w wywołaniu zwrotnym jest wywoływana po przepuszczeniu strumienia wyjściowego, a jego dane są dostępne do pobrania. – annkatrin

+1

Co oznacza "limit" w tym przykładzie? Nie jest nigdzie zadeklarowany ... – Cleankod

6

nie mogłem dostać się do pracy nad rozwiązaniem dla mnie. To właśnie opracowywane i nadzieję, że może pomóc innym:

Mój "przychodzące" przechwytujących:

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); 
    } 
} 

Mój "towarzyski" przechwytujących:

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); 
    } 
} 

Coś dodałem do mojej aplikacji Spring Framework XML kontekstu (pamiętaj, aby zdefiniować dwa przechwytywania w pliku 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> 

    ... 

Uwaga, istnieją inne sposoby dodawania przechwytywaczy, takie jak adnotacje, które umożliwiają przechwytywanie tylko określonych usług mydła. Powyższy sposób dodania przechwytywaczy "autobus" przechwycił wszystkie twoje usługi mydła.

+0

Zrobiłem dokładnie to, co sugerujesz, ale mój "loggingOutInterceptor" nie został przetestowany. Dla 'loggingInInterceptor' działa poprawnie jako oczekiwanie. Wiesz dlaczego? Dzięki. – bryannguyen

4

Po prostu chcę udostępnić jeszcze jedną opcję, jak odbierać wiadomości przychodzące i wychodzące w tym samym czasie dla niektórych celów rejestrowania, na przykład żądania dziennika i odpowiadające odpowiedzi do bazy danych.

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(); 
} 
} 

a następnie podłączyć ten moduł obsługi z usługa

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

Działa doskonale, jeśli zastępuje 'if (outgoingMessage) {' with 'if (!outgoingMessage) {' – Ivano85

+0

Hm, powinno być" if (outgoingMessage) ". Ponieważ wiadomość przychodząca powinna być zapisana na przyszłość, a kiedy mamy wychodzące wiadomości, wtedy jesteśmy w stanie rejestrować dane, ponieważ mamy teraz dostęp zarówno do wiadomości przychodzących, jak i wychodzących. – error1009

+0

Przepraszam, masz rację, ponieważ jest to punkt końcowy: P ... Używałem go po stronie klienta, więc wychodzące wiadomości były wymaganiem w moim przypadku, a wiadomość przychodząca odpowiedzią. Bardzo to doceniam, świetne rozwiązanie ;-) – Ivano85

3

Przykład zapisu tekstu do StringBuffer z elementami mocującymi do przechwytywania niektóre właściwości niestandardowych i filtrowanie XML żądanie:

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; 
    } 
} 

Aby uzyskać w pełni działający przykład, z wyjściowymi przechwytującymi, zobacz mój CXF module na github.

Powiązane problemy