CXF: как повторно отправить исходный запрос от клиента CXF?

Я использую CXF в контексте разработки SOA.

Мне интересно, есть ли у моей проблемы решение с CXF. Вот моя потребность. Мы разработали веб-приложение, обслуживающее конечные точки JAXWS, реализации конечных точек заключаются в анализе запроса через перехватчики, сохранении данных из запроса в базу данных из сервисного уровня на Java и повторной отправке исходного запроса на другой сервер через клиент CXF. Дело в том, что некоторые наши запросы содержат подпись DSIG (https://www.w3.org/TR/xmldsig-core/) или подписанное утверждение SAML. Нам нужно повторно отправлять запросы без их изменения (например, прокси) из CXFClient. CXF использует для отправки упорядоченного объекта на сервер, но таким образом исходный поток не отправляется

Есть ли способ повторно отправить входящий запрос с сервисного уровня из Java CXFClient без его изменения (подписи зависят от формата запроса: пробелы, префиксы пространств имен, возврат каретки…)? Мы предпочитаем CXFClient, потому что хотели бы повторно использовать наш самодельный перехватчик CXF, который регистрирует исходящий запрос.

Мы протестировали перехватчик, намеревающийся заменить outputStream исходным запросом перед его отправкой на сервер, мы использовали этот ответ: Как изменить необработанное XML-сообщение исходящего запроса CXF?, но мы по-прежнему нокаутируем, CXF по-прежнему отправляет поток, созданный из маршалируемого объекта. См. код ниже.

Контекст: - CXF 2.7.18 (JDK 6) и 3.1.10 (JDK 8) - Платформа: Windows 7 64-битная/rhel 7 64-битная - Apache Tomcat 7 - Tcpdump для анализа входящего трафика

Пример кода нашего клиента:

    final Client cxfClient = org.apache.cxf.frontend.ClientProxy.getClient( portType );
    cxfClient.getInInterceptors().clear();
cxfClient.getOutInterceptors().clear();
cxfClient.getOutFaultInterceptors().clear();
cxfClient.getRequestContext().put(CustomStreamerInterceptor.STREAM_TO_SEND,
PhaseInterceptorChain.getCurrentMessage().getContent( InputStream.class ) );
cxfClient.getOutInterceptors().add( new CustomStreamerInterceptor() );
org.apache.cxf.transport.http.HTTPConduit http = (org.apache.cxf.transport.http.HTTPConduit) cxfClient.getConduit();
...
port.doSomething(someRequest);

CustomStreamerInterceptor:

package test;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.io.IOUtils;
import org.apache.cxf.binding.soap.interceptor.SoapOutInterceptor.SoapOutEndingInterceptor;
import org.apache.cxf.helpers.LoadingByteArrayOutputStream;
import org.apache.cxf.interceptor.AbstractOutDatabindingInterceptor;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.io.CacheAndWriteOutputStream;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.Phase;
public class CustomStreamerInterceptor extends AbstractOutDatabindingInterceptor {
       public static final String STREAM_TO_SEND = "STREAM_TO_SEND";
       public CustomStreamerInterceptor () {
             super( Phase.WRITE_ENDING );
             addAfter( SoapOutEndingInterceptor.class.getName() );
       }
       @Override
       public void handleMessage( Message message ) throws Fault {
             try {
                    InputStream toSend = (InputStream) message.get( STREAM_TO_SEND );
                    if ( toSend != null ) {
                           toSend.reset();
                           LoadingByteArrayOutputStream lBos = new LoadingByteArrayOutputStream();
                           IOUtils.copy( toSend, lBos );
                           CacheAndWriteOutputStream cawos = (CacheAndWriteOutputStream) message.getContent( OutputStream.class );
                           cawos.resetOut( lBos, false );//fail !
                    }
             }
             catch ( Exception e ) {
                    throw new Fault( e );
             }
       }
}

Спасибо вам за любую помощь, это было бы очень полезно.


person Jeff3181    schedule 22.02.2017    source источник


Ответы (1)


Я думаю, что лучше создать «классический» HTTP-клиент, потому что CXF не предназначен для такой ситуации, его чаще используют для маршалинга объектов из java в XML... К счастью для вас, я решаю эту проблему с помощью перехватчика. . Вы можете написать перехватчик, который копирует поток в объект выходного потока, который CXF готовится отправить на сервер. Вы должны быть осторожны с фазой и порядком ваших перехватчиков, потому что, если вы используете перехватчик Logging, вы можете захотеть регистрировать исходящий поток. Этот перехватчик может выполнять эту работу, убедитесь, что он запускается после любого перехватчика регистрации. Код для CXF 2.7.18:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.lang.CharEncoding;
import org.apache.cxf.helpers.IOUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.interceptor.StaxOutInterceptor;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PassePlatClientInterceptorOut extends AbstractPhaseInterceptor<Message> {
private static final Logger LOG = LoggerFactory.getLogger( PassePlatClientInterceptorOut.class );

private final Exchange exchangeToReadFrom;

public PassePlatClientInterceptorOut( final Exchange exchange ) {
    super( Phase.PRE_STREAM );
    addBefore( StaxOutInterceptor.class.getName() );
    this.exchangeToReadFrom = exchange;
}

@Override
public void handleMessage( Message message ) {
    InputStream is = (InputStream) exchangeToReadFrom.get( PassePlatServerInterceptorIn.PASSE_PLAT_INTERCEPTOR_STREAM_SERVEUR );
    if ( is != null ) {
        message.put( org.apache.cxf.message.Message.ENCODING, CharEncoding.UTF_8 );
        OutputStream os = message.getContent( OutputStream.class );
        try {
            IOUtils.copy( is, os );
            is.close();
        }
        catch ( IOException e ) {
            LOG.error( "Error ...", e );
            message.setContent( Exception.class, e );
            throw new Fault( new Exception( "Error ...", e ) );
        }
        boolean everythingOK = message.getInterceptorChain().doInterceptStartingAt( message,
                org.apache.cxf.interceptor.MessageSenderInterceptor.MessageSenderEndingInterceptor.class.getName() );
        if ( !everythingOK ) {
            LOG.error( "Error ?" );
            throw new Fault( new Exception( "Error ..." ) );
        }
    }
}

}

Чтобы создать перехватчик:

cxfClient.getInInterceptors().add( new PassePlatClientInterceptorIn( exchange ) );
person mapperCoderZ    schedule 27.03.2017