Our WS service client inserts additional information to request headers using
an instance of AbstractOutDatabindingInterceptor. We are experiencing a
problem that the additional information is not added to request header when
chunking happens, but it works when there is no chunking.
Below is the interceptor code and cxf client configuration.
How we do it:
1. CacheAndWriteOutputStream is used with the interceptor
2. The code for writing to header is within the onClose() method of
CachedOutputStreamCallback which is registered with the above output stream
What we understand:
1. When there is no chunking, the onClose() of the callback is called, and
additional info is added to headers, when
MessageSenderInterceptor$MessageSenderEndingInterceptor is invoked. The
onClose() of the callback is triggered by calling HttpConduit.close() from
the interceptor’s handleMessage(). This interceptor is the last one in the
interceptor chain.
2. When there is chunking, all chunks but the last one are sent when
SoapOutInterceptor$SoapOutEndingInterCeptor is invoked. This interceptor is
after StaxOutInterceptor but before MessageSenderEndingInterceptor mentioned
above.
3. The last chunk is written when when
MessageSenderInterceptor$MessageSenderEndingInterceptor is handled.
So the problems appears to be that with chunking, the onClose() of the
callback, and thus the code inserting to headers, are called after the first
chunks being sent. We would like to find out what would be a good, or the
beast way of addressing this issue. Any help would be appreciated.
Thanks.
public class MyInterceptor extends AbstractOutDatabindingInterceptor {
public MyInterceptor() {
super(Phase.PRE_STREAM);
addAfter(LoggingOutInterceptor.class.getName());
addBefore(StaxOutInterceptor.class.getName());
@Override
public void handleMessage(Message message) {
try {
final OutputStream os = message.getContent(OutputStream.class);
final Writer iowriter = message.getContent(Writer.class);
if (os == null && iowriter == null) {
return;
if (os != null) {
final CacheAndWriteOutputStream newOut = new
CacheAndWriteOutputStream(os);
message.setContent(OutputStream.class, newOut);
newOut.registerCallback(new MyCallback(message, os));
} else {
//
} catch (Exception ce) {
throw new Fault(ce);
class MyCallback implements CachedOutputStreamCallback {
private final Message message;
private final OutputStream origStream;
public MyCallback(final Message msg, final OutputStream os) {
this.message = msg;
this.origStream = os;
@Override
public void onFlush(CachedOutputStream cos) {
// Nothing to do here
@Override
public void onClose(CachedOutputStream cos) {
StringBuilder buffer = new StringBuilder(1024);
try {
String encoding = (String) message.get(Message.ENCODING);
Map<String, List<String>> headers = (Map<String,
List<String>>) message.get(Message.PROTOCOL_HEADERS);
ExtraHeaderInformation extraHeaderInformation = // an instance
of extra header information
//Writing to header with extra info
headers.put(X_TIMESTAMP, Arrays.asList(new String[]
{extraHeaderInformation.getTimestamp()}));
} catch (Exception ex) {
LOGGER.error(ex.getMessage(), ex);
throw new Fault(ex);
try {
cos.lockOutputStream();
cos.resetOut(null, false);
} catch (Exception ex) {
LOGGER.error(ex.getMessage(), ex);
throw new Fault(ex);
message.setContent(OutputStream.class, origStream);
<http:conduit name="*.http-conduit">
<http:client ConnectionTimeout="600000" ReceiveTimeout="600000"
ChunkingThreshold="4000"/>
</http:conduit>
<bean id="myInterceptor"
class="MyInterceptor">
</bean>
<jaxws:client id="myClient"
serviceClass="MyServicePortType"
xmlns:svc="http://myservice/wsdl/v1" serviceName="svc:MyService"
endpointName="svc:MyServicePort" address="${service.url}"
wsdlLocation="classpath:myservice.wsdl">
<jaxws:properties>
<entry key="schema-validation-enabled" value="true" />
<entry key="org.apache.cxf.http.no_io_exceptions" value="true" />
</jaxws:properties>
<jaxws:inInterceptors>
<ref bean="httpHeaderInInterceptor" />
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<ref bean="MyInterceptor" />
</jaxws:outInterceptors>
</jaxws:client>
an instance of AbstractOutDatabindingInterceptor. We are experiencing a
problem that the additional information is not added to request header when
chunking happens, but it works when there is no chunking.
Below is the interceptor code and cxf client configuration.
How we do it:
1. CacheAndWriteOutputStream is used with the interceptor
2. The code for writing to header is within the onClose() method of
CachedOutputStreamCallback which is registered with the above output stream
What we understand:
1. When there is no chunking, the onClose() of the callback is called, and
additional info is added to headers, when
MessageSenderInterceptor$MessageSenderEndingInterceptor is invoked. The
onClose() of the callback is triggered by calling HttpConduit.close() from
the interceptor’s handleMessage(). This interceptor is the last one in the
interceptor chain.
2. When there is chunking, all chunks but the last one are sent when
SoapOutInterceptor$SoapOutEndingInterCeptor is invoked. This interceptor is
after StaxOutInterceptor but before MessageSenderEndingInterceptor mentioned
above.
3. The last chunk is written when when
MessageSenderInterceptor$MessageSenderEndingInterceptor is handled.
So the problems appears to be that with chunking, the onClose() of the
callback, and thus the code inserting to headers, are called after the first
chunks being sent. We would like to find out what would be a good, or the
beast way of addressing this issue. Any help would be appreciated.
Thanks.
public class MyInterceptor extends AbstractOutDatabindingInterceptor {
public MyInterceptor() {
super(Phase.PRE_STREAM);
addAfter(LoggingOutInterceptor.class.getName());
addBefore(StaxOutInterceptor.class.getName());
@Override
public void handleMessage(Message message) {
try {
final OutputStream os = message.getContent(OutputStream.class);
final Writer iowriter = message.getContent(Writer.class);
if (os == null && iowriter == null) {
return;
if (os != null) {
final CacheAndWriteOutputStream newOut = new
CacheAndWriteOutputStream(os);
message.setContent(OutputStream.class, newOut);
newOut.registerCallback(new MyCallback(message, os));
} else {
//
} catch (Exception ce) {
throw new Fault(ce);
class MyCallback implements CachedOutputStreamCallback {
private final Message message;
private final OutputStream origStream;
public MyCallback(final Message msg, final OutputStream os) {
this.message = msg;
this.origStream = os;
@Override
public void onFlush(CachedOutputStream cos) {
// Nothing to do here
@Override
public void onClose(CachedOutputStream cos) {
StringBuilder buffer = new StringBuilder(1024);
try {
String encoding = (String) message.get(Message.ENCODING);
Map<String, List<String>> headers = (Map<String,
List<String>>) message.get(Message.PROTOCOL_HEADERS);
ExtraHeaderInformation extraHeaderInformation = // an instance
of extra header information
//Writing to header with extra info
headers.put(X_TIMESTAMP, Arrays.asList(new String[]
{extraHeaderInformation.getTimestamp()}));
} catch (Exception ex) {
LOGGER.error(ex.getMessage(), ex);
throw new Fault(ex);
try {
cos.lockOutputStream();
cos.resetOut(null, false);
} catch (Exception ex) {
LOGGER.error(ex.getMessage(), ex);
throw new Fault(ex);
message.setContent(OutputStream.class, origStream);
<http:conduit name="*.http-conduit">
<http:client ConnectionTimeout="600000" ReceiveTimeout="600000"
ChunkingThreshold="4000"/>
</http:conduit>
<bean id="myInterceptor"
class="MyInterceptor">
</bean>
<jaxws:client id="myClient"
serviceClass="MyServicePortType"
xmlns:svc="http://myservice/wsdl/v1" serviceName="svc:MyService"
endpointName="svc:MyServicePort" address="${service.url}"
wsdlLocation="classpath:myservice.wsdl">
<jaxws:properties>
<entry key="schema-validation-enabled" value="true" />
<entry key="org.apache.cxf.http.no_io_exceptions" value="true" />
</jaxws:properties>
<jaxws:inInterceptors>
<ref bean="httpHeaderInInterceptor" />
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<ref bean="MyInterceptor" />
</jaxws:outInterceptors>
</jaxws:client>