Thursday, March 12, 2009

How to get operation name in JAX-WS handler

With web services at times one needs to know the name of the method (operation) being invoked before it is really invoked. A typical scenario is the authorization, where access to method is restricted to certain roles only. The JAX-WS RI 2.0 used to hold the name of the operation being invoked in SOAPMessageContext, which can be obtained in following manner-

soapMessageContext.get(MessageContext.WSDL_OPERATION);

Unfortunately JAX-WS RI 2.1.x no longer have this information and JAX-WS team is not ready to provide it as they said this is optional. Then what is the workaround when someone really needs it? Looking for clues I ran JAX-WS RI 2.1.5 code in Eclispe debugger and found operation name in non-public classes / variables as shown in the following screenshot-



Now equipped with this information operation name can be extracted using reflection API-

    private String getMethodName(SOAPMessageContext context, boolean isRequest) {
    try {
        Field field = context.getClass().getSuperclass().getDeclaredField(
            "packet");
        field.setAccessible(true);
        Packet packet = (Packet) field.get(context);

        if (isRequest)
        return ((StreamMessage) packet.getMessage())
            .getPayloadLocalPart();

        return ((JAXBMessage) packet.getMessage()).getPayloadLocalPart();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
    }
Though this is not a clean way and may not work with future releases of JAX-WS RI but works for now :-)

4 Comments:

Dmitry said...

Your solution works only for rpc operations, where name of first element in payload equal to operation name. If operation has style=document your will get name of element, not name of operation.

However I found solution using your code, here it is:


private QName getOperationName(SOAPMessageContext context) {
try {
QName operationName = null;
if (context.get(MessageContext.WSDL_OPERATION) != null) {
operationName = (QName) context.get(MessageContext.WSDL_OPERATION);
} else {
Field field = context.getClass().getSuperclass().getDeclaredField("packet");
field.setAccessible(true);
Packet packet = (Packet) field.get(context);

SOAPSEIModel seiModel = ((com.sun.xml.ws.client.sei.SEIStub) context
.get(BindingProviderProperties.JAXWS_CLIENT_HANDLE_PROPERTY)).seiModel;

String operationLocalName = packet.getMessage().getMethod(seiModel).getOperationName();
String portNamespace = ((QName)context.get(MessageContext.WSDL_PORT)).getNamespaceURI();
operationName = new QName(portNamespace, operationLocalName);
}
return operationName;
} catch (Exception e) {
throw new RuntimeException("Error while trying to get wsdl operation name", e);
}
}


It returns wsdl operation's QName for both rpc and document style operations.

So thank you for publish your solution :)

Anonymous said...

Dmitry thanks for the update. This was an quick and dirty solution for the problem in hand.

Besides the problem you mentioned the way mentioned here works only for server side handlers not for client handlers as there are different classes being used by JAX-WS RI on client side. Anyway good news is that JAX-WS RI 2.2 will again provide operation name, hence no more dirty tricks :-)

Anshu Gaind said...

The following article states that the WSDL_OPERATION property is optional and might be presented only if the binding has information about WSDL metadata.

http://www.javaworld.com/javaworld/jw-02-2007/jw-02-handler.html

Anonymous said...

what if I want to get the arguments passed in the operation? How do I perform that?