我一直在摆弄 CXF 上的服务器端拦截器。但似乎实现简单的传入和传出拦截器并不是一项简单的任务,它给了我一个包含 SOAP XML 的纯字符串。
我需要在拦截器中有纯 XML,以便我可以将它们用于特定的日志记录任务。标准的 LogIn & LogOut 拦截器不能胜任这项任务。是否有人愿意分享一些示例,说明我如何实现一个简单的传入拦截器,该拦截器能够获取传入的 SOAP XML 和传出拦截器以再次获取 SOAP XML?
我一直在摆弄 CXF 上的服务器端拦截器。但似乎实现简单的传入和传出拦截器并不是一项简单的任务,它给了我一个包含 SOAP XML 的纯字符串。
我需要在拦截器中有纯 XML,以便我可以将它们用于特定的日志记录任务。标准的 LogIn & LogOut 拦截器不能胜任这项任务。是否有人愿意分享一些示例,说明我如何实现一个简单的传入拦截器,该拦截器能够获取传入的 SOAP XML 和传出拦截器以再次获取 SOAP XML?
在此处找到传入拦截器的代码: Logging request/response with Apache CXF as XML
我的传出拦截器:
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) {
}
}
}
}
我无法让上述解决方案为我工作。这是我开发的,希望它可以帮助其他人:
我的“传入”拦截器:
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);
}
}
我的“传出”拦截器:
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);
}
}
我添加到我的 spring 框架应用程序上下文 XML 中的东西(记得在 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>
...
请注意,还有其他方法可以添加拦截器,例如通过注释,这将允许您仅拦截特定的肥皂服务。将拦截器添加到“总线”的上述方式将拦截您所有的肥皂服务。
我只想再分享一个选项,如何同时获取传入和传出消息以用于某些记录目的,例如记录请求和对数据库的相应响应。
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();
}
}
然后将该处理程序与网络服务连接
<jaxws:endpoint id="wsEndpoint" implementor="#myWS" address="/myWS" >
<jaxws:handlers>
<bean class="com.package.handlers.CxfLoggingHandler"/>
</jaxws:handlers>
</jaxws:endpoint>
将文本写入 StringBuffer 的示例,带有用于捕获一些自定义属性和过滤请求 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;
}
}
有关带有输出拦截器的完整工作示例,请参阅我在 github 上的CXF 模块。