Example of handler that passes out request / response message bodies:
public class MsgLogger implements SOAPHandler<SOAPMessageContext> {
public static String REQEST_BODY = "com.evil.request";
public static String RESPONSE_BODY = "com.evil.response";
@Override
public Set<QName> getHeaders() {
return null;
}
@Override
public boolean handleMessage(SOAPMessageContext context) {
SOAPMessage msg = context.getMessage();
Boolean beforeRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(32_000);
context.getMessage().writeTo(baos);
String key = beforeRequest ? REQEST_BODY : RESPONSE_BODY;
context.put(key, baos.toString("UTF-8"));
context.setScope(key, MessageContext.Scope.APPLICATION);
} catch (SOAPException | IOException e) { }
return true;
}
@Override
public boolean handleFault(SOAPMessageContext context) {
return true;
}
@Override
public void close(MessageContext context) { }
}
To register handler and use preserved properties:
BindingProvider provider = (BindingProvider) port;
List<Handler> handlerChain = bindingProvider.getBinding().getHandlerChain();
handlerChain.add(new MsgLogger());
bindingProvider.getBinding().setHandlerChain(handlerChain);
Req req = ...;
Rsp rsp = port.serviceCall(req); // call WS Port
// Access saved message bodies:
Map<String, Object> responseContext = provider.getResponseContext();
String reqBody = (String) responseContext.get(MsgLogger.REQEST_BODY);
String rspBody = (String) responseContext.get(MsgLogger.RESPONSE_BODY);
TL;DR
Metro JAX WS RI docs says about MessageContext.Scope.APPLICATION
property:
The message context object can also hold properties set by the client or provider. For instance, port proxy and dispatch objects both extend BindingProvider
. A message context object can be obtained from both to represent the request or response context. Properties set in the request context can be read by the handlers, and the handlers may set properties on the message context objects passed to them. If these properties are set with the scope MessageContext.Scope.APPLICATION
then they will be available in the response context to the client. On the server end, a context object is passed into the invoke method of a Provider
.
metro-jax-ws/jaxws-ri/rt/src/main/java/com/sun/xml/ws/api/message/Packet.java
contains property:
/**
* Lazily created set of handler-scope property names.
*
* <p>
* We expect that this is only used when handlers are present
* and they explicitly set some handler-scope values.
*
* @see #getHandlerScopePropertyNames(boolean)
*/
private Set<String> handlerScopePropertyNames;
On other hand metro-jax-ws/jaxws-ri/rt/src/main/java/com/sun/xml/ws/client/ResponseContext.java
is an implementation of Map
with:
public boolean containsKey(Object key) {
if(packet.supports(key))
return packet.containsKey(key); // strongly typed
if(packet.invocationProperties.containsKey(key))
// if handler-scope, hide it
return !packet.getHandlerScopePropertyNames(true).contains(key);
return false;
}
In SOAPHandler
we can mark property as APPLICATION
instead of default MessageContext.Scope.HANDLER
:
/**
* Property scope. Properties scoped as <code>APPLICATION</code> are
* visible to handlers,
* client applications and service endpoints; properties scoped as
* <code>HANDLER</code>
* are only normally visible to handlers.
*/
public enum Scope {APPLICATION, HANDLER};
by:
/**
* Sets the scope of a property.
*
* @param name Name of the property associated with the
* <code>MessageContext</code>
* @param scope Desired scope of the property
* @throws java.lang.IllegalArgumentException if an illegal
* property name is specified
*/
public void setScope(String name, Scope scope);