Возможно ли зарегистрировать запрос/ответ как XML с использованием CXF, в идеале, в отдельный файл, чтобы я мог контролировать, что делает приложение?
Запрос/ответ на ведение журнала с Apache CXF в формате XML
Ответ 1
Добавьте к своим конечным точкам и клиентам следующее:
<jaxws:features>
<bean class="org.apache.cxf.feature.LoggingFeature" />
</jaxws:features>
Это будет записывать все в журнал сервера.
Если вы хотите зарегистрировать их в другом месте, посмотрите на исходный код встроенного CQF LoggingInInterceptor и LoggingOutInterceptor. Вы можете следить за шаблоном, который они используют, чтобы захватить сообщения на своем пути в/из и делать с ними то, что вам нравится.
Добавьте свои собственные перехватчики в цепочку с чем-то вроде этого:
<jaxws:inInterceptors>
<ref bean="myLoggingInInterceptor" />
</jaxws:inInterceptors>
Ответ 2
Итак, я попробовал немного больше с этим. Чтобы получить запрос XML и ответы, и если вы используете Log4J, вам необходимо установить уровень журнала CXF в файле log4j.xml, подобном этому ( >= INFO):
<logger name="org.apache.cxf" >
<level value="INFO" />
</logger>
И файл cxf.xml должен содержать следующее:
<cxf:bus>
<cxf:features>
<cxf:logging/>
</cxf:features>
</cxf:bus>
Оба файла должны быть в CLASSPATH.
Чтобы отобразить сообщение о мыле, добавьте его в свой код:
Client client = ClientProxy.getClient(service);
client.getInInterceptors().add(new LoggingInInterceptor());
client.getOutInterceptors().add(new LoggingOutInterceptor());
Ответ 3
Мыло xap запроса может быть легко зарегистрировано обычным в перехватчике. Скажем, у нас есть перехватчик с именем "wsLoggingInInterceptor", поэтому в файле контекста он будет выглядеть следующим образом:
<bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor"/>
<bean id="logOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor"/>
<bean id="wsLoggingInInterceptor" class="org.jinouts.webservice.logging.WSLoggingInInterceptor"/>
<cxf:bus>
<cxf:inInterceptors>
<ref bean="loggingInInterceptor"/>
<ref bean="wsLoggingInInterceptor"/>
</cxf:inInterceptors>
<cxf:outInterceptors>
<ref bean="logOutInterceptor"/>
</cxf:outInterceptors>
</cxf:bus>
В классе мы можем получить запрос xml следующим образом:
public class WSLoggingInInterceptor extends AbstractSoapInterceptor
{
public WSLoggingInInterceptor ()
{
super(Phase.RECEIVE);
}
@Override
public void handleMessage ( SoapMessage message ) throws Fault
{
//get the remote address
HttpServletRequest httpRequest = (HttpServletRequest) message.get ( AbstractHTTPDestination.HTTP_REQUEST );
System.out.println ("Request From the address : " + httpRequest.getRemoteAddr ( ) );
try
{
// now get the request xml
InputStream is = message.getContent ( InputStream.class );
CachedOutputStream os = new CachedOutputStream ( );
IOUtils.copy ( is, os );
os.flush ( );
message.setContent ( InputStream.class, os.getInputStream ( ) );
is.close ( );
System.out.println ("The request is: " + IOUtils.toString ( os.getInputStream ( ) ));
os.close ( );
}
catch ( Exception ex )
{
ex.printStackTrace ( );
}
}
}
Посмотрите, здесь я также зарегистрирую адрес, откуда идет запрос. Вы также можете получить дополнительную информацию из объекта "HttpServletRequest". у вас может быть больше: http://cxf.apache.org/docs/interceptors.html
Чтобы зарегистрировать ответ xml, вы можете посмотреть этот поток
Ответ 4
Если вы используете Spring с его Java-конфигурацией, есть 2 простых способа активировать ведение журнала SOAP-сообщений с Apache CXF:
-
Непосредственно на SpringBus - это полезно, если вы хотите регистрировать сообщения всех своих конечных точек CXF:
@Bean(name=Bus.DEFAULT_BUS_ID) public SpringBus springBus() { SpringBus springBus = new SpringBus(); LoggingFeature logFeature = new LoggingFeature(); logFeature.setPrettyLogging(true); logFeature.initialize(springBus); springBus.getFeatures().add(logFeature); return springBus; }
-
Активировать ведение журнала отдельно на каждой открытой конечной точке CXF
@Bean public Endpoint endpoint() { EndpointImpl endpoint = new EndpointImpl(springBus(), weatherService()); endpoint.publish(SERVICE_NAME_URL_PATH); endpoint.setWsdlLocation("Weather1.0.wsdl"); LoggingFeature logFeature = new LoggingFeature(); logFeature.setPrettyLogging(true); logFeature.initialize(springBus()); endpoint.getFeatures().add(logFeature); return endpoint; }
Напомнить LoggingFeature.setPrettyLogging(true); Метод просмотра довольно печатных SOAP-сообщений и LoggingFeature.initialize(springBus()); - без последней магии не бывает. Для более чистого кода вы также можете отделить LoggingFeature как отдельный Bean и ввести его либо в свой SpringBus, либо в Endpoint- Bean.
Ответ 5
Намного проще добавить свой собственный регистратор в свойства конечной точки. В этом случае перехватчик регистрации по умолчанию будет искать ваш логгер в свойствах конечных точек, и если он найдет его, он будет использовать его, иначе он будет создавать по умолчанию. Вот мой пример использования:
<jaxws:endpoint
xmlns:client="http://service.info.client.diasoft.services.stream.integration.cib.sberbank.ru"
address="/diasoft/clientInfoWS"
serviceName="client:ClientWS"
implementor="#clientServiceImpl">
<jaxws:properties>
<entry key="MessageLogger" value-ref="logger"/>
</jaxws:properties>
<jaxws:features>
<bean class="org.apache.cxf.feature.LoggingFeature"/>
</jaxws:features>
</jaxws:endpoint>
<bean id="logger" class="org.apache.cxf.common.logging.LogUtils" factory-method="getLogger">
<constructor-arg value="ru.sberbank.cib.integration.stream.services.diasoft.client.info.service.ClientWSImpl"/>
</bean>
Ответ 6
В моем случае я должен был поддерживать сгенерированный jaxb клиент. Итак, я нашел следующую ситуацию в applicationContext:
<jaxws:client id="aService"
serviceClass="org.xxx.ServiceClass"
address="${service.url}"
username="${service.username}"
password="${service.password}">
<jaxws:features>
<bean class="org.apache.cxf.feature.LoggingFeature" />
</jaxws:features>
</jaxws:client>
И я превратил это в правду:
<jaxws:client id="aService"
address="${service.url}"
username="${service.username}"
password="${service.password}">
<jaxws:features>
<bean class="org.apache.cxf.feature.LoggingFeature" />
</jaxws:features>
<jaxws:inInterceptors>
<bean class="com.blah.blah.SoapInInterceptor"/>
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<bean class="com.blah.blah.SoapOutInterceptor"/>
</jaxws:outInterceptors>
<jaxws:inFaultInterceptors>
<bean class="com.blah.blah.SoapFaultInterceptor"/>
</jaxws:inFaultInterceptors>
</jaxws:client>
Мой класс SoapInInterceptor:
public class SoapInInterceptor extends AbstractSoapInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(SoapInInterceptor .class);
public SoapInInterceptor () {
super(Phase.RECEIVE);
}
@Override
public void handleMessage(SoapMessage message) throws Fault {
try {
InputStream is = message.getContent(InputStream.class);
CachedOutputStream os = new CachedOutputStream();
IOUtils.copy(is, os);
os.flush();
message.setContent(InputStream.class, os.getInputStream());
is.close();
LOGGER.debug("RESPONSE: {}", IOUtils.toString(os.getInputStream()));
os.close();
} catch (Exception ex) {
LOGGER.error("Error trying to log response", ex);
}
}
}
Мой класс SoapOutInterceptor:
public class SoapOutInterceptor extends AbstractSoapInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(SoapOutInterceptor .class);
public SoapOutInterceptor () {
super(Phase.PRE_STREAM);
}
@Override
public void handleMessage(SoapMessage message) throws Fault {
CacheAndWriteOutputStream cwos = new CacheAndWriteOutputStream(message.getContent(OutputStream.class));
message.setContent(OutputStream.class, cwos);
cwos.registerCallback(new LoggingOutCallBack());
}
class LoggingOutCallBack implements CachedOutputStreamCallback {
@Override
public void onClose(CachedOutputStream cos) {
try {
if (cos != null) {
LOGGER.debug("REQUEST: {}", IOUtils.toString(cos.getInputStream()));
}
} catch (Exception e) {
LOGGER.error("Error trying to log request", e);
}
}
@Override
public void onFlush(CachedOutputStream arg0) {}
}
}
И мой класс SoapFaultInterceptor (почти равный SoapInInterceptor):
public class SoapFaultInterceptor extends AbstractSoapInterceptor{
private static final Logger LOGGER = LoggerFactory.getLogger(SoapFaultInterceptor.class);
public SoapFaultInterceptor() {
super(Phase.RECEIVE);
}
@Override
public void handleMessage(SoapMessage message) throws Fault {
try {
InputStream is = message.getContent(InputStream.class);
CachedOutputStream os = new CachedOutputStream();
IOUtils.copy(is, os);
os.flush();
message.setContent(InputStream.class, os.getInputStream());
is.close();
LOGGER.debug("FAULT: {}", IOUtils.toString(os.getInputStream()));
os.close();
} catch (Exception ex) {
LOGGER.error("Error trying to log fault", ex);
}
}
}
С последним определением в приложении мне нужно было настроить файл logback.xml следующим образом:
<!—logger-->
<logger name="com.blah.blah">
<level value="DEBUG"/>
<appender-ref ref="aDailyRollingFileAppender"/>
</logger>
<!-- appender -->
<appender name="aDailyRollingFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/jboss-as-7.2.0.Final/standalone/log/soap-payloads.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/jboss-as-7.2.0.Final/standalone/log/soap-payloads-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>10</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{35} [%file:%line] - %msg %n</pattern>
</encoder>
</appender>
Чтобы все полезные данные были в одном файле.
Если вы хотите разделить его, создайте разные регистраторы/приложения, указав полное имя класса каждого перехватчика.
Для меня это решение идеально подходит для того, что я хотел.
Надеюсь, это поможет.
С уважением.