Getting Started | Consuming a SOAP web serviceを基に、SOAP Webサービスから生成したクラスを使用してサービスにアクセスする。
事前準備
手順
gradle
https://start.spring.io/ でweb, lombok, devtoolsあたりを追加して生成し、それにGetting Started | Consuming a SOAP web serviceを参考に色々とSOAP関連の設定を追加する。
実際に使用する際は、ソースコード生成先・WSDLスキーマURL・package名、あたりを変更すると思われる。他はほぼコピペでOK。
plugins { id 'org.springframework.boot' version '2.7.1' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' configurations { jaxb compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } task genJaxb { ext.sourcesDir = "${buildDir}/generated-sources/jaxb" ext.classesDir = "${buildDir}/classes/jaxb" ext.schema = "http://localhost:8080/CalculatorService/Calculator?wsdl" outputs.dir classesDir doLast() { project.ant { taskdef name: "xjc", classname: "com.sun.tools.xjc.XJCTask", classpath: configurations.jaxb.asPath mkdir(dir: sourcesDir) mkdir(dir: classesDir) xjc(destdir: sourcesDir, schema: schema, package: "com.example.consumingwebservice.wsdl") { arg(value: "-wsdl") produces(dir: sourcesDir, includes: "**/*.java") } javac(destdir: classesDir, source: 1.8, target: 1.8, debug: true, debugLevel: "lines,vars,source", classpath: configurations.jaxb.asPath) { src(path: sourcesDir) include(name: "**/*.java") include(name: "*.java") } copy(todir: classesDir) { fileset(dir: sourcesDir, erroronmissingdir: false) { exclude(name: "**/*.java") } } } } } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation 'org.springframework.ws:spring-ws-core' // For Java 11: implementation 'org.glassfish.jaxb:jaxb-runtime' implementation(files(genJaxb.classesDir).builtBy(genJaxb)) implementation 'javax.xml.soap:saaj-api:1.3.5' implementation 'com.sun.xml.messaging.saaj:saaj-impl:1.5.2' jaxb "com.sun.xml.bind:jaxb-xjc:2.1.7" } tasks.named('test') { useJUnitPlatform() }
Getting Started | Consuming a SOAP web serviceに無いjavax.xml.soap:saaj-api
, com.sun.xml.messaging.saaj:saaj-impl
は後述。
以下のようにgenJaxb
タスクでWSDLからJavaのソースコードとclassファイルが生成される。
gradlew genJaxb
application.properties
ローカルでSOAP webサービスを8080ポートで動かすのでクライアント側のwebは8081にしておく。
server.port=8081
java
サンプル実行用の適当なエンドポイントを作る。
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController @SpringBootApplication public class SoapClientMain { @Autowired CalculatorClient client; @GetMapping("/sample") public void sample() { client.sum(); } public static void main(String[] args) { SpringApplication.run(SoapClientMain.class, args); } }
Jaxb2Marshaller
とそれを使用するクライアントのbeanを定義する。実使用時にはクラス生成先のpackage名やWSDLのURLを変更する。
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.oxm.jaxb.Jaxb2Marshaller; @Configuration public class SoapClientConfig { @Bean public Jaxb2Marshaller marshaller() { Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); // this package must match the package in the <generatePackage> specified in // pom.xml marshaller.setContextPath("com.example.consumingwebservice.wsdl"); return marshaller; } @Bean public CalculatorClient client(Jaxb2Marshaller marshaller) { CalculatorClient client = new CalculatorClient(); client.setDefaultUri("http://localhost:8080/CalculatorService/Calculator.wsdl"); client.setMarshaller(marshaller); client.setUnmarshaller(marshaller); return client; } }
クライアントクラスはWebServiceGatewaySupport
を拡張し、そのクラスのメソッドを使用してSOAP Webサービスにアクセスする。
import javax.xml.bind.JAXBElement; import org.springframework.ws.client.core.support.WebServiceGatewaySupport; import com.example.consumingwebservice.wsdl.ObjectFactory; import com.example.consumingwebservice.wsdl.Sum; import com.example.consumingwebservice.wsdl.SumResponse; public class CalculatorClient extends WebServiceGatewaySupport { public void sum() { ObjectFactory factory = new ObjectFactory(); Sum r = factory.createSum(); r.setArg0(334); r.setArg1(22); JAXBElement<Sum> request = factory.createSum(r); @SuppressWarnings("unchecked") JAXBElement<SumResponse> response = (JAXBElement<SumResponse>) getWebServiceTemplate() .marshalSendAndReceive("http://localhost:8080/CalculatorService/Calculator", request); System.out.println(response.getValue().getReturn()); } }
ハマった点
java.lang.ClassNotFoundException: javax.xml.soap.SOAPException
詳細は省略するがとにかくJava 17にはこのクラスは居なくなっている。なので、何らかの依存性を追加する必要がある。ここではjavax.xml.soap:saaj-api:1.3.5
を追加しているが、おそらく他のやり方もあるように思われる(これ以上調べてない)。
Unable to create SAAJ meta-factoryProvider com.sun.xml.internal.messaging.saaj.soap.SAAJMetaFactoryImpl not found
Getting Startedには無いんだがcom.sun.xml.messaging.saaj:saaj-impl:1.5.2
を追加しないと動かない。また、META-INF/services/javax.xml.soap.MetaFactory
でクラス名の指定の必要もある。
Getting Startedにはjavax.xml.soap:saaj-api
, com.sun.xml.messaging.saaj:saaj-impl
が居ない。なので何等か俺の環境依存の問題があると思われるが、これ以上は調べていない。
Caused by: javax.xml.soap.SOAPException: Unable to create SAAJ meta-factoryProvider com.sun.xml.internal.messaging.saaj.soap.SAAJMetaFactoryImpl not found
色々ぐぐってみると、どうもsaaj-impl-x.x.x.jar
のバージョンにってSAAJMetaFactoryImpl
のpackageが違うらしい。なのでMETA-INFで指定の必要がある。使用するsaaj-impl-x.x.x.jar
の中身見るのが早いっぽい。
com.sun.xml.internal.messaging.saaj.soap.SAAJMetaFactoryImpl
com.sun.xml.messaging.saaj.soap.SAAJMetaFactoryImpl
Setting either 'contextPath', 'classesToBeBound', or 'packagesToScan' is required
Getting Started
のConfiguring Web Service Componentsのサンプルコードのとおり、Jaxb2Marshaller#setContextPath
で生成クラスのpackage名を指定する必要がある。
Caused by: java.lang.IllegalArgumentException: Setting either 'contextPath', 'classesToBeBound', or 'packagesToScan' is required
No marshaller registered
Getting Started
のConfiguring Web Service Componentsのサンプルコードのとおり、WebServiceGatewaySupport
を拡張したSOAPクライアント役のクラスにmarshallerをセットする必要がある。
Caused by: java.lang.IllegalStateException: No marshaller registered. Check configuration of WebServiceTemplate.
@XmlRootElement注釈がないため、……(以下略)
おそらくだが、このサンプルのSum
はオブジェクトでは無いのでそのまま渡せずJAXBElement<Sum>
でラップの必要がある、と思われる。spring-bootのサンプルコードとかはGetCountryRequest
という感じで使ってるし。
Caused by: com.sun.istack.SAXException2: @XmlRootElement注釈がないため、タイプ"com.example.consumingwebservice.wsdl.Sum"を要素としてマーシャリングできません