自定义SOAP响应的JAX-WS前缀

目标

我正在为一个相当古老(但可悲的是不可改变的)界面实现一个Web服务。 我有一个问题,即调用我的服务的客户端需要SOAP响应中的某个命名空间,并且我很难将其更改为匹配。

考虑一个hello world示例,我想要这个:

   Hello Catchwa!    

看起来像这样:

    Hello Catchwa!    

我找到了类似于我在这里尝试的东西,但是我无法获得类似的代码来正确执行。 (我想坚持使用Metro,而不必更改为cxf或轴)


执行

我返回JAXBRIContextJAXBContextFactory实现如下所示:

 import com.sun.xml.bind.api.JAXBRIContext; import com.sun.xml.bind.api.TypeReference; import com.sun.xml.ws.api.model.SEIModel; import com.sun.xml.ws.developer.JAXBContextFactory; import java.util.ArrayList; import java.util.List; import javax.xml.bind.JAXBException; import javax.xml.namespace.QName; public class HelloJaxbContext implements JAXBContextFactory { @Override public JAXBRIContext createJAXBContext(SEIModel seim, List classesToBind, List typeReferences) throws JAXBException { List classList = new ArrayList(); classList.addAll(classesToBind); List refList = new ArrayList(); for (TypeReference tr : typeReferences) { refList.add(new TypeReference(new QName(tr.tagName.getNamespaceURI(), tr.tagName.getLocalPart(), "customns"), tr.type, tr.annotations)); } return JAXBRIContext.newInstance(classList.toArray(new Class[classList.size()]), refList, null, seim.getTargetNamespace(), false, null); } } 

Web服务的一些测试代码很简单:

 import com.sun.xml.ws.developer.UsesJAXBContext; import javax.jws.WebService; import javax.jws.WebMethod; import javax.jws.WebParam; @WebService(serviceName = "Hello") @UsesJAXBContext(value = HelloJaxbContext.class) public class Hello { @WebMethod(operationName = "hello") public String hello(@WebParam(name = "name") String txt) { return "Hello " + txt + "!"; } } 

问题

在使用jaxws-rt 2.2.7(来自Maven)的Tomcat 7.0.32和Glassfish 3.1.2中,上面的代码不会影响我的Web服务输出(名称空间前缀仍然是“ns2”)。

如果你从旧服务的WSDL开始并使用wsimport生成所有各种JAXB带注释的请求和响应包装类,那么在生成的包中你应该找到一个package-info.java ,如

 @javax.xml.bind.annotation.XmlSchema(namespace = "http://test/") package com.example.test; 

JAXB提供了一种机制,您可以在@XmlSchema注释上建议前缀映射,因此您可以尝试修改package-info.java以进行读取

 @javax.xml.bind.annotation.XmlSchema(namespace = "http://test/", xmlns = { @javax.xml.bind.annotation.XmlNs(prefix = "customns", namespaceURI="http://test/") } ) package com.example.test; 

并查看是否对生成的消息产生任何影响。 这也具有纯JAXB规范的优点(即不依赖于RI特定的自定义上下文工厂)。

如果你需要重新运行wsimport你可以通过将-npa选项传递给xjc来阻止它覆盖你修改的package-info (这告诉它不要生成一个package-info.java ,而是把所有必要的namespace设置放在反而是类级别的注释)。 具体如何做到这一点取决于你如何运行wsimport

命令行:

 wsimport -B-npa .... 

ant:

    

Maven的:

  org.jvnet.jax-ws-commons jaxws-maven-plugin 2.2    wsimport    -npa      

实现您要实现的目标的推荐/标准方法是使用SOAPMessage Handler 。 它们类似于Java Web应用程序filter(理论上也可以在这里工作),因为它们用于实现责任链模式 。 例如,在您的情况下,您可以这样:

 import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.namespace.QName; import javax.xml.soap.SOAPBody; import javax.xml.soap.SOAPEnvelope; import javax.xml.soap.SOAPException; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPHandler; import javax.xml.ws.handler.soap.SOAPMessageContext; public class SOAPBodyHandler implements SOAPHandler { static final String DESIRED_NS_PREFIX = "customns"; static final String DESIRED_NS_URI = "http://test/"; static final String UNWANTED_NS_PREFIX = "ns"; @Override public Set getHeaders() { //do nothing return null; } @Override public boolean handleMessage(SOAPMessageContext context) { if ((boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY)) { //Check here that the message being intercepted is an outbound message from your service, otherwise ignore. try { SOAPEnvelope msg = context.getMessage().getSOAPPart().getEnvelope(); //get the SOAP Message envelope SOAPBody body = msg.getBody(); body.removeNamespaceDeclaration(UNWANTED_NS_PREFIX); body.addNamespaceDeclaration(DESIRED_NS_PREFIX, DESIRED_NS_URI); } catch (SOAPException ex) { Logger.getLogger(SOAPBodyHandler.class.getName()).log(Level.SEVERE, null, ex); } } return true; //indicates to the context to proceed with (normal)message processing } @Override public boolean handleFault(SOAPMessageContext context) { //do nothing return null; } @Override public void close(MessageContext context) { //do nothing } 

}

在Service Implementation Bean类声明中,添加

  @HandlerChain(file = "handler-chain.xml") 

上面的注释是对实际允许处理程序启动的配置文件的引用。 配置文件看起来像这样

      your.handler.FQN.here    

在家尝试一下。 此特定代码尚未经过测试

在参考实现中,我这样做是为了最终使它工作。 请参阅使用Metro编组JAXB类时忽略的schemaLocation