wsimport は JAX-WS 標準の WSDL からの Java クライアントコード生成ツールです。シンプルなサービスでは問題なく動作しますが、複雑な型定義、複数スキーマ、非標準パターンを含むエンタープライズ WSDL に遭遇した途端、難解なエラーを吐き始めます。本記事では、よく遭遇するエラーとその具体的な修正方法を解説します。
エラー 1: 「Two Declarations Cause a Collision」
複雑な WSDL で最も頻繁に遭遇する wsimport エラーです。JAXB (wsimport 内部の XML-Java バインディング層) が同じ名前の Java クラスやプロパティを 2 つ生成しようとした場合に発生します。
[ERROR] Two declarations cause a collision in the ObjectFactory class.
line 42 of file:/path/to/service.wsdl
[ERROR] (Related to above error) This is the other declaration.
line 87 of file:/path/to/service.wsdl
発生原因
JAXB は XML の要素名を命名規則に基づいて Java クラス名にマッピングします。例えば getUser (要素) と GetUser (complexType) は、どちらも Java クラス GetUser にマッピングされ、衝突が発生します。
修正方法: JAXB バインディングカスタマイズファイル
jaxb-binding.xml を作成して名前衝突を解決します:
<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
version="3.0">
<!-- 衝突している型の名前を変更 -->
<jaxb:bindings schemaLocation="service.wsdl#types?schema1">
<jaxb:bindings node="//xs:complexType[@name='GetUser']">
<jaxb:class name="GetUserType"/>
</jaxb:bindings>
</jaxb:bindings>
<!-- または全体にサフィックスルールを適用 -->
<jaxb:bindings>
<jaxb:nameXmlTransform>
<jaxb:typeName suffix="Type"/>
</jaxb:nameXmlTransform>
</jaxb:bindings>
</jaxb:bindings>
wsimport に渡して実行します:
wsimport -keep -b jaxb-binding.xml -d output/ service.wsdl
衝突が多数ある場合は、グローバルな設定がより実用的です:
<jaxb:bindings xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb" version="3.0">
<jaxb:globalBindings>
<jaxb:serializable uid="1"/>
<jaxb:javaType name="java.util.Calendar"
xmlType="xs:dateTime"
parseMethod="jakarta.xml.bind.DatatypeConverter.parseDateTime"
printMethod="jakarta.xml.bind.DatatypeConverter.printDateTime"/>
</jaxb:globalBindings>
</jaxb:bindings>
エラー 2: 「Undefined Element Declaration」
[ERROR] undefined element declaration 's:schema'
line 15 of https://api.example.com/service.asmx?wsdl
このエラーは .NET (ASMX や WCF) の Web サービスで非常に多く見られます。Microsoft 固有のスキーマ拡張や非標準のプレフィックスを使用しているスキーマを参照している場合に発生します。
修正方法: WSDL をダウンロードして修正する
# WSDL をローカルにダウンロード
curl -o service.wsdl "https://api.example.com/service.asmx?wsdl"
問題のある要素を修正します。s:schema の問題は通常、不足しているスキーマインポートの追加で解決します:
<!-- <wsdl:types> セクション内に追加 -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import namespace="http://www.w3.org/2001/XMLSchema"/>
</xs:schema>
あるいは、ベンダー拡張を有効にする -extension フラグを使用します:
wsimport -keep -extension -d output/ service.wsdl
エラー 3: エンコーディングの非対応
[ERROR] unsupported encoding: windows-1252
レガシーな WSDL ファイルが UTF-8 以外のエンコーディングを宣言していることがあります。最新のツールではサポートされていない場合があります。
修正方法: エンコーディングを変換する
# ダウンロードして UTF-8 に変換
curl -o service-original.wsdl "https://api.example.com/service?wsdl"
iconv -f WINDOWS-1252 -t UTF-8 service-original.wsdl > service.wsdl
# XML 宣言を更新
# 変更前: <?xml version="1.0" encoding="windows-1252"?>
# 変更後: <?xml version="1.0" encoding="UTF-8"?>
エラー 4: パッケージ名の衝突
WSDL が異なる名前空間を持つ複数のスキーマをインポートしている場合、wsimport は各名前空間を Java パッケージにマッピングします。2 つの名前空間が同じパッケージ名を生成するとコンパイルに失敗します。
[ERROR] Two classes have the same name
"com.example.generated.ObjectFactory"
修正方法: 明示的なパッケージマッピング
wsimport -keep \
-p com.example.orders \
-b package-binding.xml \
-d output/ \
service.wsdl
スキーマ単位で制御するバインディングファイル:
<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="3.0">
<jaxb:bindings schemaLocation="service.wsdl#types?schema1">
<jaxb:schemaBindings>
<jaxb:package name="com.example.orders.types"/>
</jaxb:schemaBindings>
</jaxb:bindings>
<jaxb:bindings schemaLocation="service.wsdl#types?schema2">
<jaxb:schemaBindings>
<jaxb:package name="com.example.orders.common"/>
</jaxb:schemaBindings>
</jaxb:bindings>
</jaxb:bindings>
エラー 5: Java 11 以降のモジュールシステム問題
Java 11 以降、JAX-WS は JDK から削除されました。以下のようなエラーが発生します:
error: package javax.xml.ws does not exist
error: package javax.jws does not exist
修正方法: Jakarta XML Web Services 依存関係を追加
Java 11 以降では外部依存関係が必要です。Maven の場合:
<dependencies>
<!-- JAX-WS ランタイム -->
<dependency>
<groupId>jakarta.xml.ws</groupId>
<artifactId>jakarta.xml.ws-api</artifactId>
<version>4.0.2</version>
</dependency>
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
<version>4.0.3</version>
<scope>runtime</scope>
</dependency>
<!-- JAXB (同じく JDK から削除済み) -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>4.0.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>4.0.5</version>
<scope>runtime</scope>
</dependency>
</dependencies>
JDK 同梱の wsimport ではなく、Jakarta ディストリビューションのスタンドアロン版を使用します:
<!-- Maven プラグインで wsimport を実行 -->
<plugin>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<version>4.0.3</version>
<executions>
<execution>
<goals><goal>wsimport</goal></goals>
<configuration>
<wsdlUrls>
<wsdlUrl>https://api.example.com/service?wsdl</wsdlUrl>
</wsdlUrls>
<packageName>com.example.service</packageName>
<keep>true</keep>
<extension>true</extension>
<bindingFiles>
<bindingFile>${basedir}/src/main/resources/jaxb-binding.xml</bindingFile>
</bindingFiles>
</configuration>
</execution>
</executions>
</plugin>
Gradle の場合:
plugins {
id 'java'
}
configurations {
jaxws
}
dependencies {
jaxws 'com.sun.xml.ws:jaxws-tools:4.0.3'
implementation 'jakarta.xml.ws:jakarta.xml.ws-api:4.0.2'
runtimeOnly 'com.sun.xml.ws:jaxws-rt:4.0.3'
}
tasks.register('wsimport', JavaExec) {
classpath = configurations.jaxws
mainClass = 'com.sun.tools.ws.WsImport'
args '-keep',
'-extension',
'-d', "${buildDir}/generated-sources/jaxws",
'-p', 'com.example.service',
"${projectDir}/src/main/resources/wsdl/service.wsdl"
}
compileJava.dependsOn wsimport
sourceSets.main.java.srcDir "${buildDir}/generated-sources/jaxws"
エラー 6: wsdlLocation のランタイムパス問題
生成されたコードには、ハードコードされたパスを含む @WebServiceClient アノテーションが含まれます:
@WebServiceClient(name = "OrderService",
wsdlLocation = "file:/Users/dev/project/service.wsdl")
public class OrderService extends Service {
このパスは他のマシンや本番環境では動作しません。
修正方法: wsdlLocation を上書きする
wsimport -keep \
-wsdllocation /wsdl/OrderService.wsdl \
-d output/ \
service.wsdl
WSDL をクラスパス (src/main/resources/wsdl/) に配置し、実行時にロードします:
URL wsdlUrl = OrderService.class.getResource("/wsdl/OrderService.wsdl");
OrderService service = new OrderService(wsdlUrl);
OrderServicePortType port = service.getOrderServicePort();
トラブルシューティングチェックリスト
| 症状 | 原因 | 修正方法 |
|---|---|---|
| Two declarations cause collision | 要素名/型名の重複 | JAXB バインディングファイルで名前変更 |
undefined element s:schema | .NET WSDL の固有仕様 | -extension フラグまたは WSDL 修正 |
| Unsupported encoding | レガシーエンコーディング | iconv で UTF-8 に変換 |
| ObjectFactory の同名クラス | 名前空間→パッケージ変換の衝突 | 明示的パッケージバインディング |
package javax.xml.ws does not exist | Java 11+ で JAX-WS 削除 | Jakarta 依存関係を追加 |
| 実行時に WSDL が見つからない | ハードコードされた wsdlLocation | -wsdllocation フラグ |
| 生成中にタイムアウト | リモート WSDL に到達不可 | ローカルダウンロード + カタログ |
SOAPless による解決
本記事で取り上げたすべてのエラーは、同じ根本的な問題に起因しています。WSDL ファイルからの Java クライアントコード生成は本質的に脆弱だということです。スキーマの変更は生成コードを壊し、名前衝突にはバインディングファイルが必要で、Java バージョンのアップグレードには依存関係の移行が伴います。
SOAPless はまったく異なるアプローチを取ります。クライアントコードを生成する代わりに、WSDL URL を SOAPless ダッシュボードに登録するだけで REST JSON エンドポイントが自動生成されます。アプリケーションは標準的な JSON を HTTP で送受信するだけです。生成スタブも JAXB バインディングも wsimport も不要です。JSON から SOAP XML への変換、名前空間管理、スキーマ解決はすべてサーバーサイドで処理されます。
SOAP 認証情報は AES-256-GCM 暗号化で保護され、登録されたすべてのサービスには OpenAPI 3.0 ドキュメントが自動生成されます。ダッシュボードからコードを書く前に直接オペレーションをテストできます。