运行 开发或生产模式下的 JAX-WS Web 服务

Running a JAX-WS web service in development or production mode

我有一个使用 wsimport 生成的 Web 服务(在 ANT 任务中),我想为其处理发送无效 XML 时出现的 javax.xml.bind.UnmarshalException 异常请求。

为了抛出这个异常,我只是向服务器发送了一个不平衡的标签,然后我返回:

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
 <S:Body>
      <S:Fault xmlns:ns4="http://www.w3.org/2003/05/soap-envelope">
         <faultcode>S:Server</faultcode>
         <faultstring>javax.xml.bind.UnmarshalException
 - with linked exception:
[com.ctc.wstx.exc.WstxParsingException: Unexpected close tag &lt;/m:BAR>; expected &lt;/m:BR>.
 at [row,col {unknown-source}]: [16,38]]</faultstring>
      </S:Fault>
   </S:Body>
</S:Envelope>

不幸的是,在我的代码中我看不到捕获此异常并发回标准消息的方法,而不是自动发送客户端可能无法解析的一些 web 服务内部异常。

有没有办法告诉 JAX-Ws 在 生产开发 模式下 运行?在 production 模式下,它允许我覆盖为回复错误请求而发送的消息吗?

或者至少实现某种挂钩,当 JAX-WS 代码中发生异常时调用我的方法...

我运行在 Glassfish 4.1.2 上安装服务。

我用于从 wsdl 构建网络服务的 ANT 脚本如下所示:

<!DOCTYPE project>
<project name="WSDL to Java generator" default="wsimport">  
    <path id="wsimport.classpath">
        <fileset dir="../../EAR/EarContent/lib">
            <include name="commons-beanutils-*.jar"/>
            <include name="commons-lang-*.jar"/>
            <include name="commons-logging-*.jar"/>
        </fileset>

        <fileset dir="../../Common/ant-libs">
            <include name="*.jar"/>
        </fileset>
    </path>

    <taskdef name="wsimport" classname="com.sun.tools.ws.ant.WsImport" classpathref="wsimport.classpath"/>

    <target name="wsimport">
        <wsimport verbose="true" wsdl="../schemas/WebService.wsdl"  wsdlLocation="../schemas/WebService.wsdl" binding="./ws-build-bindings.xjb">
            <xjcarg line="-Xannotate"/>
            <xjcarg line="-Xinheritance"/>

            <arg line="-keep"/>
            <arg line="-d ../src"/>
            <arg line="-p org.company.name.message"/>

            <arg line="-Xnocompile"/>    
        </wsimport>
    </target>
</project>

生成的界面如下所示:

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.Holder;
/**
 * This class was generated by the JAX-WS RI.
 * JAX-WS RI 2.3.0
 * Generated source version: 2.2
 * 
 */
@WebService(name = "WebService", targetNamespace = "http://company.com/foo/WebService/")
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
@XmlSeeAlso({
    ObjectFactory.class
})
public interface WebService {

    @WebMethod(operationName = "PublishEvent", action = "PublishEvent")
    @WebResult(name = "ResponseMessage", targetNamespace = "http://foo.bar/schema/message", partName = "response")
    public ResponseMessageType publishEvent(
        @WebParam(name = "EventMessage", targetNamespace = "http://foo.bar/schema/message", partName = "event")
        EventMessageType event);

    @WebMethod(operationName = "Request", action = "Request")
    @WebResult(name = "ResponseMessage", targetNamespace = "http://foo.bar/schema/message", partName = "response")
    public ResponseMessageType request(
        @WebParam(name = "RequestMessage", targetNamespace = "http://foo.bar/schema/message", partName = "request")
        RequestMessageType request);

    @WebMethod(operationName = "Response", action = "Response")
    public void response(
        @WebParam(name = "ResponseMessage", targetNamespace = "http://foo.bar/schema/message", mode = WebParam.Mode.INOUT, partName = "response")
        Holder<ResponseMessageType> response);

}

生成的class扩展Service:

import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.WebServiceFeature;    
/**
 * This class was generated by the JAX-WS RI.
 * JAX-WS RI 2.3.0
 * Generated source version: 2.2
 * 
 */
@WebServiceClient(name = "WebService", targetNamespace = "http://company.com/foo/WebService/", wsdlLocation = "../schemas/WebService.wsdl")
public class WebService_Service
    extends Service
{

    private final static URL WebSERVICE_WSDL_LOCATION;
    private final static WebServiceException WebSERVICE_EXCEPTION;
    private final static QName WebSERVICE_QNAME = new QName("http://company.com/foo/WebService/", "WebService");

    static {
        WebSERVICE_WSDL_LOCATION = org.company.name.message.WebService_Service.class.getResource("../schemas/WebService.wsdl");
        WebServiceException e = null;
        if (WebSERVICE_WSDL_LOCATION == null) {
            e = new WebServiceException("Cannot find '../schemas/WebService.wsdl' wsdl. Place the resource correctly in the classpath.");
        }
        WebSERVICE_EXCEPTION = e;
    }

    public WebService_Service() {
        super(__getWsdlLocation(), WebSERVICE_QNAME);
    }

    public WebService_Service(WebServiceFeature... features) {
        super(__getWsdlLocation(), WebSERVICE_QNAME, features);
    }

    public WebService_Service(URL wsdlLocation) {
        super(wsdlLocation, WebSERVICE_QNAME);
    }

    public WebService_Service(URL wsdlLocation, WebServiceFeature... features) {
        super(wsdlLocation, WebSERVICE_QNAME, features);
    }

    public WebService_Service(URL wsdlLocation, QName serviceName) {
        super(wsdlLocation, serviceName);
    }

    public WebService_Service(URL wsdlLocation, QName serviceName, WebServiceFeature... features) {
        super(wsdlLocation, serviceName, features);
    }

    @WebEndpoint(name = "WebServiceSOAP")
    public WebService getWebServiceSOAP() {
        return super.getPort(new QName("http://company.com/foo/WebService/", "WebServiceSOAP"), WebService.class);
    }

    @WebEndpoint(name = "WebServiceSOAP")
    public WebService getWebServiceSOAP(WebServiceFeature... features) {
        return super.getPort(new QName("http://company.com/foo/WebService/", "WebServiceSOAP"), WebService.class, features);
    }

    private static URL __getWsdlLocation() {
        if (WebSERVICE_EXCEPTION!= null) {
            throw WebSERVICE_EXCEPTION;
        }
        return WebSERVICE_WSDL_LOCATION;
    }

}

这是我对接口的实现(省略了实际工作的代码),当发出无效请求时,我无法捕获它,因为我的以下方法未被调用:

import javax.jws.WebService;
import javax.xml.ws.Holder;

import org.company.name.message.ErrorType.ErrorLevelEnum;
import org.company.name.message.EventMessageType;
import org.company.name.message.HeaderType;
import org.company.name.message.WebService;
import org.company.name.message.RequestMessageType;
import org.company.name.message.ResponseMessageType;

@WebService(endpointInterface = "org.company.name.message.WebService")
public class Web implements WebService {
    @Override
    public ResponseMessageType publishEvent(EventMessageType event) {
        ResponseMessageBuilder responseBuilder = new ResponseMessageBuilder();
        try
        {
            // build response here

        }
        catch (WebException exception) {
            // error building response
        }
        catch (Exception ex) {
            // general error
        }

        return responseBuilder.build();
    }

    @Override
    public ResponseMessageType request(RequestMessageType request) {
        ResponseMessageBuilder responseBuilder = new ResponseMessageBuilder();

        try
        {
            // build response here

        }
        catch (WebException exception) {
            // error building response
        }
        catch (Exception ex) {
            // general error
        }

        return responseBuilder.build();
    }

    @Override
    public void response(Holder<ResponseMessageType> response) {
        // TODO Auto-generated method stub
    }
}

使用 SOAPHandler (https://docs.oracle.com/middleware/1213/wls/WSGET/jax-ws-soaphandlers.htm#WSGET3446) 解决了这个问题。

处理程序拦截请求,尝试 unmarshal 它(通过 JAXB),如果抛出异常,我自定义响应消息(即通过在handleMessage 您的 SOAPHandler 方法)并将其发回(通过从 handleMessage 方法返回 false

此外,值得研究一下 SOAPFaultException class (https://docs.oracle.com/javase/7/docs/api/javax/xml/ws/soap/SOAPFaultException.html),您可以抛出它并根据您的喜好自定义 soap 故障响应消息。