Apache Woden 1.0M10 WSDL 解析 API 详解

详解 Apache Woden 1.0M10 的 WSDL 解析 API:WSDLFactory 与 WSDLReader 工厂体系、readWSDL 多入口、Description 组件模型、ErrorReporter 错误处理链、元素层与组件层双视图及 WSDLWriter 回写,所有方法调用与回显均实测通过。

Apache WodenApache Woden2026/10/15

Apache Woden 1.0M10 WSDL 解析 API 详解

目标:系统讲解 Apache Woden 1.0M10(org.apache.woden:woden-core)的 WSDL 解析 API——工厂与读取器、三种读取入口、Description 组件模型、错误处理链、元素层/组件层双视图与 WSDLWriter 回写;本文每个方法调用与回显均实测通过(实测日期 2026-10-15)。

Woden 1.0M10 是一个纯 DOM 实现的 WSDL 2.0 解析库,编程面集中在 org.apache.woden 与 org.apache.woden.wsdl20 两个包:前者提供工厂、读取器与错误处理骨架(共 271 个类中的核心入口),后者是 WSDL 2.0 规范组件模型到 Java 接口的映射。读懂两条主线即可掌握——「读取主线」从 WSDLFactory 到 Description,「错误主线」从 ErrorReporter 到 ErrorHandler。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64)
  • JDK:OpenJDK 17.0.20.1
  • 构建工具:Apache Maven 3.6.3
  • 组件版本:Apache Woden 1.0M10(org.apache.woden:woden-core:1.0M10,Maven Central 唯一版本,2015-09 发布,2015 年后无更新、项目低频维护)
  • 传递依赖:xmlschema-core 2.2.1、commons-logging 1.1.1
  • 工程与示例文件:/root/woden-demo、/root/wsdl/StockQuote.wsdl(最小 WSDL 2.0 示例,见第 3 篇第二章)

💡 技术拓展:WSDL 2.0 与 WSDL 1.1 是两个规范,Woden 是 WSDL 2.0 专用解析器(喂 1.1 会报错误码 WSDL514,实测见第 3 篇第五章)。WSDL 2.0 把文档分成两个视图:元素层(XML 元素与属性,对应 *.xml 包)与组件层(语义对象,对应 wsdl20 包)。本文采用 Woden 1.0M10 组件层 API 为主、元素层为辅 的讲解视角。

二、API 总览

Woden 解析链路上的核心接口一览(全部位于 org.apache.woden / org.apache.woden.wsdl20,签名自实测反编译核对):

接口/类职责实测落地实现
WSDLFactory工厂入口,newInstance() 静态创建org.apache.woden.internal.DOMWSDLFactory
WSDLReader读取 WSDL,产出 Descriptionorg.apache.woden.internal.DOMWSDLReader
WSDLSourcereadWSDL 的流/DOM 载体DOMWSDLSource
ErrorReporter错误上报中枢,挂 ErrorHandler、管 Localeorg.apache.woden.internal.ErrorReporterImpl
ErrorHandler应用侧错误回调(warning/error/fatalError)应用实现
ErrorInfo单条错误:key、消息、定位器ErrorInfoImpl
ErrorLocator行号/列号/公共标识符ErrorLocatorImpl
WSDLException致命错误的受检异常,带 faultCode直接抛出
Description组件模型根(规范里的 Definitions)DescriptionImpl
Interface / InterfaceOperation / InterfaceFault / InterfaceMessageReference抽象接口层组件*Impl
Binding / BindingOperation / BindingFault协议绑定层组件*Impl
Service / Endpoint服务与端点组件*Impl
WSDLWriter把组件模型序列化回 XMLorg.apache.woden.internal.DOMWSDLWriter
ExtensionRegistry扩展元素/属性注册表PopulatedExtensionRegistry

关系图(文字版):WSDLFactory →(newWSDLReader)→ WSDLReader →(readWSDL)→ Description →(toElement)→ DescriptionElement;WSDLReader 持有 ErrorReporter,ErrorReporter 持有 ErrorHandler,解析期每条错误以 ErrorInfo 送达回调,致命错误另抛 WSDLException。

三、工厂与读取器:解析链路的起点

3.1 WSDLFactory.newInstance 的机制

WSDLFactory.newInstance() 是整个 API 的静态入口,内部按顺序探测:系统属性 org.apache.woden.factoryImplName → DOMWSDLFactory(1.0M10 唯一内置实现)。实测落点:

WSDLFactory factory = WSDLFactory.newInstance();
System.out.println(factory.getClass().getName());
org.apache.woden.internal.DOMWSDLFactory

工厂提供三个创建方法(签名实测):newWSDLReader()(解析器)、newWSDLWriter()(序列化器)、newPopulatedExtensionRegistry()(扩展注册表)。若需显式指定实现类,newInstance(String factoryImplName) 支持传入 org.apache.woden.internal.DOMWSDLFactory。

3.2 WSDLReader 的完整方法面

WSDLReader 接口的全部方法(实测 javap 核对):

方法用途
Description readWSDL(String wsdlURL)入口一:按 URL 字符串读取(file:/http: 协议均可)
Description readWSDL(WSDLSource source)入口二:从 WSDLSource 携带的 DOM/InputSource 读取
WSDLSource createWSDLSource()创建空 WSDLSource
ErrorReporter getErrorReporter()取错误上报中枢(挂回调、设 Locale 都在它身上)
void setFeature(String, boolean) / getFeature(String)行为开关(见下文实测)
void setProperty(String, Object) / getProperty(String)解析器与类型系统属性
void setFactoryImplName(String) / getFactoryImplName()读取器实现切换
void setExtensionRegistry(ExtensionRegistry) / getExtensionRegistry()扩展注册表替换
void setURIResolver(URIResolver) / getURIResolver()自定义 URI 解析(import 重定向等)

feature 常量与实测行为是 1.0M10 最容易踩的坑:接口声明了 FEATURE_VERBOSE、FEATURE_VALIDATION、FEATURE_CONTINUE_ON_ERROR 三个常量,但实现只认 FEATURE_VALIDATION。实测证据:

# 传入 FEATURE_CONTINUE_ON_ERROR 的实测回显
Exception in thread "main" java.lang.IllegalArgumentException: The feature name "http://ws.apache.org/woden/features/continue_on_error" is not recognized.
// 默认值与合法开关(实测)
System.out.println(reader.getFeature(WSDLReader.FEATURE_VALIDATION)); // false
reader.setFeature(WSDLReader.FEATURE_VALIDATION, true);               // 合法

因此「解析不中断、错误攒起来」不要指望 continue_on_error 开关——正确姿势是给 ErrorReporter 挂自定义 ErrorHandler(第四章)。

3.3 readWSDL 的两种入口实测

入口一是最常用的 URL 字符串;入口二 WSDLSource 适合「内容已在内存/自定义流」的场景。两者实测代码与回显(文件 WsdlEntries.java):

WSDLFactory factory = WSDLFactory.newInstance();
WSDLReader reader = factory.newWSDLReader();

// 入口一:URL 字符串(file:/http: 均可)
Description d1 = reader.readWSDL(args[0]);
System.out.println("入口一 readWSDL(String) OK, services=" + d1.getServices().length);

// 入口二:WSDLSource 携带 DOM Document + 基 URI
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);                       // 必须开启命名空间感知
Document doc = dbf.newDocumentBuilder().parse(new FileInputStream(args[0]));
WSDLSource source = reader.createWSDLSource();
source.setSource(doc);
source.setBaseURI(new java.net.URI(args[0]));      // 相对 import 以此为基准
Description d2 = reader.readWSDL(source);
System.out.println("入口二 readWSDL(WSDLSource/DOM) OK, services=" + d2.getServices().length
        + " targetNS=" + d2.toElement().getTargetNamespace());
入口一 readWSDL(String) OK, services=1
入口二 readWSDL(WSDLSource/DOM) OK, services=1 targetNS=http://example.org/wsdl20/stockquote
ENTRIES_OK

两个实测注意点:DocumentBuilderFactory 必须先 setNamespaceAware(true),否则 Woden 读不到元素命名空间直接按 WSDL 1.1 误判;setBaseURI 决定相对 import 的解析基准,缺失时相对引用无法定位。

四、错误处理体系:ErrorReporter / ErrorHandler / WSDLException

Woden 的错误处理分两条路径:可继续的错误走 ErrorReporter → ErrorHandler 回调(ErrorInfo 载体),致命错误直接抛 WSDLException(带 faultCode)。

4.1 挂载 ErrorHandler 收集错误

回调接口只有一个实现点,三个方法按严重级分类。实测骨架(含定位信息提取):

reader.getErrorReporter().setErrorHandler(new ErrorHandler() {
    public void warning(ErrorInfo e)    { record("WARN ", e); }
    public void error(ErrorInfo e)      { record("ERROR", e); }
    public void fatalError(ErrorInfo e) { record("FATAL", e); }
});

static void record(String level, ErrorInfo e) {
    ErrorLocator loc = e.getErrorLocator();               // 行列号可能为 null
    int line = loc == null ? -1 : loc.getLineNumber();
    int col  = loc == null ? -1 : loc.getColumnNumber();
    System.out.println("[" + level + "] 行:" + line + " 列:" + col
            + " key=" + e.getKey() + " | " + e.getMessage());
}

对一个缺失 targetNamespace 的坏文件实测(完整坏文件与解析见第 5 篇),错误条目带真实行列号:

[ERROR] 行:4 列:50 key=cvc-complex-type.4 | cvc-complex-type.4: Attribute 'targetNamespace' must appear on element 'description'.

实测要点:ErrorInfo.getKey() 返回错误码(XSD 校验错误是 XML Schema 约束键如 cvc-complex-type.4,Woden 自身语义错误是 WSDL5xx 系列);fatalError 回调之后读取仍会以 WSDLException 中止,两条路径不是互斥的。

4.2 WSDLException 的 faultCode 族

致命异常的 getFaultCode() 实测取值:

faultCode触发场景(实测)
INVALID_WSDL文档根本不是 WSDL 2.0(如喂入 WSDL 1.1 报 WSDL514)
PARSER_ERRORXML 解析失败、IO 不可达
OTHER_ERROR其余实现级错误

最典型的实测样本(Axis 官方 WSDL 1.1 示例直读):

WSDLException: faultCode=INVALID_WSDL: Fatal WSDL error:
0:0,WSDL514,It appears as though you are trying to parse a WSDL 1.1 document but Woden is a WSDL 2.0 parser. Please check the namespace of the document's document element, or try another tool such as the Woden tool to convert from WSDL 1.1 to WSDL 2.0.

4.3 错误消息的本地化实测

ErrorReporter 提供 setLocale(Locale) 与 getFormattedMessage(key, args),但实测 1.0M10 只内置英文消息资源。完整实测(文件 WsdlLocale.java):

System.out.println("缺省 Locale: " + reader.getErrorReporter().getLocale());
reader.getErrorReporter().setLocale(java.util.Locale.CHINA);
System.out.println("设置后 Locale: " + reader.getErrorReporter().getLocale());
System.out.println("WSDL514 消息: " + reader.getErrorReporter().getFormattedMessage("WSDL514", null));
缺省 Locale: null
设置后 Locale: zh_CN
WSDL514 消息: It appears as though you are trying to parse a WSDL 1.1 document but Woden is a WSDL 2.0 parser. Please check the namespace of the document's document element, or try another tool such as the Woden tool to convert from WSDL 1.1 to WSDL 2.0.

实测结论:getLocale() 缺省为 null;setLocale 只改内部状态,getFormattedMessage 仍返回英文——错误消息没有中文资源包,做本地化需自行按 key 映射。

五、Description 组件模型:从根向下遍历

readWSDL 产出的 Description 是 WSDL 2.0 组件模型的根,五类子组件的获取方法(实测签名):

方法返回对应 WSDL 2.0 规范组件
getInterfaces() / getInterface(QName)Interface[] / InterfaceInterface
getBindings() / getBinding(QName)Binding[] / BindingBinding
getServices() / getService(QName)Service[] / ServiceService
getElementDeclarations() / getElementDeclaration(QName)ElementDeclaration[] / ElementDeclarationElement Declaration(types 内的 xs:element)
getTypeDefinitions() / getTypeDefinition(QName)TypeDefinition[] / TypeDefinitionType Definition(types 内的 xs:schema 类型)

按「Interface → InterfaceOperation → 消息引用」与「Service → Endpoint」两条线遍历的实测程序与回显(复用第 3 篇的 WsdlParse):

目标命名空间: http://example.org/wsdl20/stockquote
Interface 数: 1
  Interface: {http://example.org/wsdl20/stockquote}StockQuoteInterface
    Operation: {http://example.org/wsdl20/stockquote}GetQuote mep=http://www.w3.org/ns/wsdl/in-out
Binding  数: 1
  Binding: {http://example.org/wsdl20/stockquote}StockQuoteSOAPBinding type=http://www.w3.org/ns/wsdl/soap
Service  数: 1
  Service: {http://example.org/wsdl20/stockquote}StockQuoteService implements={http://example.org/wsdl20/stockquote}StockQuoteInterface
    Endpoint: StockQuoteEndpoint address=http://example.org:8080/stockquote binding={http://example.org/wsdl20/stockquote}StockQuoteSOAPBinding

各层关键方法速查(全部实测可用):

  • Interface:getName()、getInterfaceOperations()、getInterfaceFaults()、extendsInterface()。
  • InterfaceOperation:getName()、getMessageExchangePattern()(8 种标准 MEP,如 http://www.w3.org/ns/wsdl/in-out)、getInterfaceMessageReferences()(input/output 消息)、getInterfaceFaultReferences()。
  • InterfaceMessageReference:getMessageLabel()、getDirection()(Direction.IN/OUT 枚举)、getMessageContentModel()(#element 等)。
  • Binding:getName()、getType()(绑定类型命名空间,SOAP 为 http://www.w3.org/ns/wsdl/soap)、getBindingOperations()、getInterface()。
  • Service:getName()、getInterface()、getEndpoints()。
  • Endpoint:getName()、getAddress()、getBinding()。

💡 注意:Service.getInterface() 与 Endpoint.getBinding() 在目标引用悬空(指向不存在的组件)时返回 null 且 1.0M10 默认校验不报错——这是生产代码必须判空的真实行为,实测细节与防御方案见第 5 篇第三章。

六、元素层与组件层:同一文档的两个视图

Description 只暴露组件层;toElement() 切换到元素层 DescriptionElement,可见规范里的每一个 XML 细节。两层的对应关系(实测):

组件层元素层
DescriptionDescriptionElement
InterfaceInterfaceElement
InterfaceOperationInterfaceOperationElement
BindingBindingElement
ServiceServiceElement
EndpointEndpointElement

元素层的实测能力(复用 WsdlParse 中两处调用):

Description desc = reader.readWSDL(path);
System.out.println("目标命名空间: " + desc.toElement().getTargetNamespace());
DescriptionElement de = desc.toElement();
System.out.println("元素层 getServiceElements()= " + de.getServiceElements().length);
System.out.println("元素层 getInterfaceElements()= " + de.getInterfaceElements().length);
System.out.println("元素层 getDocumentationElements()= " + de.getDocumentationElements().length);
元素层 getServiceElements()= 1
元素层 getInterfaceElements()= 1
元素层 getDocumentationElements()= 1

选择建议:统计、过滤、按规范语义导航用组件层;要读 documentation、扩展属性、规范未建模的原始 XML 属性(如 wsoap:safe)用元素层。扩展属性的元素层读取实测见第 5 篇第四章。

七、WSDLWriter:组件模型回写 XML

WSDLFactory.newWSDLWriter() 得到 WSDLWriter,把 DescriptionElement 序列化回 XML(Writer 与 OutputStream 两种重载)。实测代码与回显(文件 WsdlTraverse.java 第 5 段):

WSDLWriter writer = factory.newWSDLWriter();
StringWriter sw = new StringWriter();
writer.writeWSDL(desc.toElement(), sw);
String xml = sw.toString();
System.out.println("回写字节数 = " + xml.length());
System.out.println("回写后仍含 <service = " + xml.contains("<service"));
System.out.println("回写后仍含 StockQuoteEndpoint = " + xml.contains("StockQuoteEndpoint"));
回写字节数 = 1972
回写后仍含 <service = true
回写后仍含 StockQuoteEndpoint = true

实测结论:round-trip 保真——解析后再回写,service、endpoint 等元素完整保留;「读入 → 校验/改写 → 回写」的工程化流水线可用这一对 Reader/Writer 搭建。

八、API 速查与注意事项

最小依赖 classpath(实测):woden-core-1.0M10.jar + xmlschema-core-2.2.1.jar + commons-logging-1.1.1.jar,三个 jar 即可运行全部本文示例。

解析流程模板(可直接复制):

WSDLFactory factory = WSDLFactory.newInstance();          // DOMWSDLFactory
WSDLReader reader = factory.newWSDLReader();              // DOMWSDLReader
reader.setFeature(WSDLReader.FEATURE_VALIDATION, true);   // 唯一受支持的开关,可选
reader.getErrorReporter().setErrorHandler(yourHandler);   // 错误收集,建议必挂
Description desc = reader.readWSDL("file:///root/wsdl/StockQuote.wsdl");

五条实测注意事项:

  1. 1.0M10 只认 FEATURE_VALIDATION 一个 feature,FEATURE_VERBOSE/FEATURE_CONTINUE_ON_ERROR 传入即 IllegalArgumentException(3.2 节实测)。
  2. 错误回调挂 ErrorReporter 而不是 WSDLReader;ErrorInfo.getKey() 是错误码,ErrorLocator 可能缺省,判空后再取行列号(4.1 节实测)。
  3. 组件名一律 {namespace}localName 形态,getName() 返回 javax.xml.namespace.QName,比较时用 equals 而非字符串拼接。
  4. 元素层 DOM 自建入口必须 setNamespaceAware(true),否则根元素命名空间读不到(3.3 节实测踩坑)。
  5. InterfaceFault.getElementDeclaration()、InterfaceMessageReference.getElementDeclaration() 的返回值依赖元素 QName 的真实可解析性:QName 指向的元素声明存在时正常返回声明对象;QName 悬空(命名空间不一致或未声明)时返回 null 且 1.0M10 校验不报错,需按第 5 篇第五章的两步法自查。

九、参考链接

  1. Apache Woden 官方主页(含用户文档):http://ws.apache.org/woden/
  2. 官方 bin 包内离线 Javadoc(docs/apidocs):https://archive.apache.org/dist/ws/woden/1.0M10/
  3. Maven 中央仓库 woden-core:https://repo1.maven.org/maven2/org/apache/woden/woden-core/
  4. W3C WSDL 2.0 规范(组件模型定义):https://www.w3.org/TR/wsdl20/
  5. W3C WSDL 2.0 规范入门(Primer):https://www.w3.org/TR/wsdl20-primer/
  6. W3C WSDL 2.0 附加规范(MEP 清单):https://www.w3.org/TR/wsdl20-additional/