Menguasai SOAP Fault sebagai mekanisme error standar: anatomi faultcode faultstring dan detail pada SOAP 1.1 dibanding 1.2, memetakan error bisnis ke kode konsisten, membungkus exception dengan WebFault, dan strategi logging error yang dapat dilacak.

Setelah di episode 7 kita menguasai tipe data dan validasi, sekarang kita bahas apa yang terjadi ketika operasi gagal. Dalam SOAP, error bukan pesan teks bebas atau HTML — ia adalah SOAP Fault, struktur standar yang bisa dibaca mesin dan dipetakan ke kode yang konsisten.
Mengapa penting? Karena di integrasi B2B, error yang tidak terstruktur adalah bencana: sistem penagihan tidak tahu apakah transaksi gagal karena input salah, karena server sibuk, atau karena akun tidak ditemukan. Dengan Fault yang terstruktur, client bisa memutuskan retry, menampilkan pesan yang benar, atau menandai transaksi untuk review manual — secara otomatis.
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>Akun tidak ditemukan</faultstring>
<detail>
<e:error xmlns:e="urn:example:bank">ACC-001 tidak dikenal</e:error>
</detail>
</soap:Fault>
</soap:Body>
</soap:Envelope>Empat elemen SOAP 1.1:
| Elemen | Wajib? | Isi |
|---|---|---|
faultcode | Ya | Kode klasifikasi error |
faultstring | Ya | Deskripsi singkat untuk manusia |
faultactor | Opsional | Pelaku error (intermediary) |
detail | Opsional | Detail aplikasi/error bisnis |
SOAP 1.2 merombak struktur Fault menjadi lebih rapi:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Body>
<soap:Fault>
<soap:Code>
<soap:Value>soap:Receiver</soap:Value>
</soap:Code>
<soap:Reason>
<soap:Text xml:lang="id">Akun tidak ditemukan</soap:Text>
</soap:Reason>
<soap:Detail>
<e:error xmlns:e="urn:example:bank">
<e:code>ACC_NOT_FOUND</e:code>
</e:error>
</soap:Detail>
</soap:Fault>
</soap:Body>
</soap:Envelope>Perbedaan kunci: faultcode diganti Code (nilai terstruktur), faultstring diganti Reason (mendukung multi-bahasa via xml:lang), dan faultactor diganti Role (opsional).
| Kode 1.1 | Nilai 1.2 | Arti |
|---|---|---|
soap:VersionMismatch | soap:VersionMismatch | Namespace SOAP salah/tidak dikenal |
soap:MustUnderstand | soap:MustUnderstand | Header mustUnderstand tidak dipahami |
soap:Client | soap:Sender | Kesalahan di sisi pengirim (request invalid) |
soap:Server | soap:Receiver | Kesalahan di sisi penerima (server gagal proses) |
| (custom) | (custom) | Kode aplikasi, mis. ACC_NOT_FOUND |
Aturan praktis: Sender/Client artinya jangan retry (perbaiki request dulu); Receiver/Server artinya boleh retry (masalah di server). Client yang baik memakai pembeda ini untuk memutuskan retry policy — tema yang kita dalami di episode 12.
Di sistem nyata, error bisnis lebih banyak daripada faultcode standar. Buatlah katalog error aplikasi yang konsisten dan didokumentasikan — ini kontrak yang setara pentingnya dengan WSDL:
| Kode | HTTP analog | Retry? | Contoh pesan |
|---|---|---|---|
INVALID_INPUT | 400 | Tidak | Format accountNo salah |
ACC_NOT_FOUND | 404 | Tidak | Akun tidak ditemukan |
INSUFFICIENT_BALANCE | 422 | Tidak | Saldo tidak cukup |
DUPLICATE_TX | 409 | Tidak | Transaksi sudah ada (idempotency) |
SERVICE_BUSY | 503 | Ya | Backend sibuk, coba lagi |
INTERNAL_ERROR | 500 | Ya | Exception tak terduga |
Kode ini dikirim di elemen detail (detail aplikasi), sementara faultcode tetap mengikuti klasifikasi SOAP. Client membaca detail untuk logika bisnis dan faultcode untuk keputusan retry.
Di JAX-WS, error bisnis dipetakan ke SOAP Fault dengan class exception yang beranotasi @WebFault:
package id.devnull.soap;
import jakarta.xml.ws.WebFault;
@WebFault(name = "BankFault", targetNamespace = "urn:example:bank")
public class BankFault extends Exception {
private final BankFaultInfo faultInfo;
public BankFault(String message, BankFaultInfo faultInfo) {
super(message);
this.faultInfo = faultInfo;
}
public BankFaultInfo getFaultInfo() {
return faultInfo;
}
}package id.devnull.soap;
public class BankFaultInfo {
private String code; // mis. ACC_NOT_FOUND
private String message; // pesan untuk client
}Service melempar exception ini untuk error bisnis yang diprediksi:
@WebMethod
public BigDecimal getBalance(String accountNo) throws BankFault {
if (accountNo == null || accountNo.isBlank()) {
throw new BankFault("Input tidak valid",
new BankFaultInfo("INVALID_INPUT", "accountNo wajib diisi"));
}
if (!"ACC-001".equals(accountNo)) {
throw new BankFault("Akun tidak ditemukan",
new BankFaultInfo("ACC_NOT_FOUND", accountNo + " tidak dikenal"));
}
return new BigDecimal("2750000.00");
}Tip
Batas antara error bisnis (Fault) dan exception tak terduga (Receiver): jika error bisa diprediksi dari input bisnis, gunakan @WebFault; jika exception tidak terduga (NPE, koneksi DB putus), biarkan framework memetakannya menjadi soap:Receiver dan log detailnya di server.
Fault yang baik harus bisa dilacak. Standar praktik untuk error handling SOAP:
INVALID_INPUT mengalir ke dashboard bisnis; INTERNAL_ERROR mengalir ke on-call engineer.Inti yang harus dibawa pulang:
faultcode/faultstring/detail (1.1) vs Code/Reason/Detail (1.2).Sender/Client = jangan retry; Receiver/Server = boleh retry.@WebFault memetakan exception bisnis menjadi Fault terstruktur di JAX-WS.Di episode 9 selanjutnya kita akan membahas WS-Addressing & WS-ReliableMessaging — endpoint reference dan wsa:Action untuk routing serta async reply, jaminan pengiriman dengan sequence WS-RM, dan pentingnya idempotency. Sampai jumpa di episode 9!