はじめに
SpringBootでのSOAPクライアント実装方法について記載します。
あまり使われなくなったSOAPですが、たまに使うので備忘として。
環境
- Spring Boot 2.3
- spring-ws-core
- Java 8
ライブラリの追加
spring-ws-coreを依存関係に追加します。
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-core</artifactId>
</dependency>
WSDLからソース生成
maven-jaxb2-pluginを使用します。
pom.xmlに下記を定義し、実行することでSOAPサービスのRequest/ResponseのBeanが生成されます。
データ定義をXSDに切り出している場合にも対応しています。
#Gradle版がなくすみません…。いつか試して追記したいです。
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- WSDLからJAXBのソースを生成するためのプラグイン -->
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.14.0</version>
<executions>
<!-- 以下、webサービスごとに定義 -->
<execution>
<id>サービスのID(任意の値)executions内で一意</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<!-- WSDLから生成する旨の宣言 -->
<schemaLanguage>WSDL</schemaLanguage>
<!-- 生成元となるWSDLとXSDの格納先 ※WSDLとXSDで同一ディレクトリ前提 -->
<schemaDirectory>${project.basedir}/wsdl/exampleSoap</schemaDirectory>
<!-- WSDLからXSDを参照している場合に必要となるスキーマ参照定義 -->
<schemaIncludes>
<include>exampleSoap.wsdl</include>
</schemaIncludes>
<!-- ソース生成先パッケージ -->
<generatePackage>exampleSoap.api.client.exampleSoap.dto</generatePackage>
<!-- ソース生成先ディレクトリ -->
<generateDirectory>${project.basedir}/src/main/java</generateDirectory>
<!-- 生成するソースのgenerateコメントにタイムスタンプを設定しないようにする(true:設定しない) -->
<noFileHeader>true</noFileHeader>
<!-- 生成後のディレクトリ削除要否(false:削除しない) -->
<clearOutputDir>false</clearOutputDir>
<!-- episodeファイル生成要否(false:生成しない) ※デフォルトはtrueなので抑止 -->
<episode>false</episode>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
SOAPクライアントクラスの作成
WebServiceGatewaySupportを継承したクラスを作成します。
marshalSendAndReceiveでSOAP通信を実行しています。
リクエストのBeanのインスタンスはObjectFactoryで生成します。ObjectFactoryはWSDLからのソース生成時にBeanと一緒に生成されます。
@Service
public class ExampleSoapClientService extends WebServiceGatewaySupport {
@SuppressWarnings("unchecked")
public ExampleSoapResponse execute() throws WebServiceException {
// RequestのBean作成
ObjectFactory objectFactory = new ObjectFactory();
ExampleSoapRequest reqBean = objectFactory.createExampleSoapRequest();
reqBean.setUserId("user0001");
reqBean.setName("サンプル太郎");
reqBean.setAge("25");
// ...省略
// 作成したリクエストのBeanをJAXBElementにラップする
JAXBElement<ExampleSoapRequest> request = objectFactory.createExampleSoap(reqBean);
JAXBElement<ExampleSoapResponse> response = null;
// SOAP通信実行
try {
response = (JAXBElement<ExampleSoapResponse>) getWebServiceTemplate().marshalSendAndReceive(request);
} catch ( SoapFaultClientException e ) {
// Faultエラーが返ってきた場合の処理
// e.getSoapFault().getFaultDetail()でFault電文の<detail>タグ内の情報を取得できる
} catch ( WebServiceException e ) {
// その他のSOAP通信エラーが発生した場合の処理
}
return response.getValue(); // getValue()でResponseのルート要素のBeanを取得
}
}
※補足
説明と関係無いため省略していますが、クラス内で@Value
や@Autowired
を使っているためサービスクラス(@Service
)として作成しています。
他のコンポーネントをDIする必要がなければこのクラス自体はSpringアノテーションはつけず、後述のConfigクラスでBean定義する形がSpring公式のサンプルにも則っているので綺麗だと思います。
JAXBマーシャラのBean定義とSOAPクライアントクラスの設定
JAXBマーシャラをBean定義し、先ほど作成したSOAPクライアントクラスへ設定します。
エンドポイントの設定もここで行っています。
※今回呼出すSOAPサービスはBasic認証があるため下記サンプル内でBasic認証に関する設定もしていますが、認証なしの場合は不要です。
######SOAPクライアントクラスをサービスクラス(@Service
)で作成した場合
@Configuration
public class SoapClientConfig {
/** SOAPエンドポイント */
@Value("${exampleSoap.endpointUri}")
private String exampleSoapEndpointUri;
/** Basic認証情報 ※認証なしの場合は不要 */
@Value("${exampleSoap.basicAuthPassword")
private String basicAuthPass;
/** SOAPクライアントクラス(WebServiceGatewaySupportのサブクラス) */
@Autowired
private ExampleSoapClientService exampleSoapClientService;
/** JAXBマーシャラのBean定義 */
@Bean
public Jaxb2Marshaller exampleSoapMarshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
// SoapのBean格納パッケージを指定
// 可変長引数なのでアプリケーション内で複数のSOAPサービスを呼出す場合はカンマ区切りで指定
marshaller.setPackagesToScan("exampleSoap.api.client.exampleSoap.dto");
// SOAPクライアントクラスの設定
exampleSoapClientService.setMarshaller(marshaller); // マーシャラの設定
exampleSoapClientService.setUnmarshaller(marshaller); // アンマーシャラの設定
exampleSoapClientService.setDefaultUri(exampleSoapEndpointUri); // エンドポイントの設定
// Basic認証をする場合の設定 ※認証なしの場合は不要
exampleSoapClientService.getWebServiceTemplate().setMessageSender(new HttpUrlConnectionMessageSender() {
@Override
protected void prepareConnection(HttpURLConnection connection) throws IOException {
// パスワードをBase64エンコードしてリクエストヘッダに追加する
String password = basicAuthPass;
String encodedPass = Base64.getEncoder().encodeToString(password.getBytes());
connection.setRequestProperty("Authorization", "Basic" + encodedPass);
super.prepareConnection(connection);
}
});
return marshaller;
}
}
######SOAPクライアントクラスをSpringアノテーションをつけずに作成した場合
@Configuration
public class SoapClientConfig {
/** SOAPエンドポイント */
@Value("${exampleSoap.endpointUri}")
private String exampleSoapEndpointUri;
/** Basic認証情報 ※認証なしの場合は不要 */
@Value("${exampleSoap.basicAuthPassword")
private String basicAuthPass;
/** JAXBマーシャラのBean定義 */
@Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
// SoapのBean格納パッケージを指定
// 可変長引数なのでアプリケーション内で複数のSOAPサービスを呼出す場合はカンマ区切りで指定
marshaller.setPackagesToScan("exampleSoap.api.client.exampleSoap.dto");
return marshaller;
}
/** SOAPクライアントクラスのBean定義 */
@Bean
public ExampleSoapClientService exampleSoapClientService (Jaxb2Marshaller marshaller) {
ExampleSoapClientService exampleSoapClientService = new ExampleSoapClientService();
// SOAPクライアントクラスの設定
exampleSoapClientService.setMarshaller(marshaller); // マーシャラの設定
exampleSoapClientService.setUnmarshaller(marshaller); // アンマーシャラの設定
exampleSoapClientService.setDefaultUri(exampleSoapEndpointUri); // エンドポイントの設定
// Basic認証をする場合の設定 ※認証なしの場合は不要
exampleSoapClientService.getWebServiceTemplate().setMessageSender(new HttpUrlConnectionMessageSender() {
@Override
protected void prepareConnection(HttpURLConnection connection) throws IOException {
// パスワードをBase64エンコードしてリクエストヘッダに追加する
String password = basicAuthPass;
String encodedPass = Base64.getEncoder().encodeToString(password.getBytes());
connection.setRequestProperty("Authorization", "Basic" + encodedPass);
super.prepareConnection(connection);
}
});
return exampleSoapClientService;
}
}
リクエスト/レスポンス電文をログ出力する
application.ymlに以下を定義することで出力されます。
logging:
level:
org.springframework.ws.client.MessageTracing.sent: TRACE
org.springframework.ws.client.MessageTracing.received: TRACE