← Zur Uebersicht

SOAP: Service-Implementierung und Client-Aufruf

Serverseite: @WebService

Beispiel: BillingServiceEndpoint in novaris-billing-soap.

@WebService(endpointInterface = "com.novaris.legacy.common.billing.contract.BillingServicePort",
        targetNamespace = "http://billing.novaris.com/soap",
        serviceName = "BillingService",
        portName = "BillingServicePort")
public class BillingServiceEndpoint implements BillingServicePort {
    @Override
    public String registerPolicy(String policyNumber, long annualPremiumCents)
            throws BillingRegistrationFault { ... }
}

endpointInterface verweist auf das SEI - alle Web-Service-relevanten Metadaten (Operationsnamen, Parameter-Namen, Namespace, Fault-Zuordnung) kommen von dort; die Implementierungsklasse selbst traegt inhaltlich nur die Fachlogik. Diese Trennung (Interface = Vertrag, Implementierung = Verhalten) ist derselbe Gedanke wie bei PolicyManagementLocal/PolicyManagementBean in novaris-policy-core - nur hier fuer einen SOAP- statt einen EJB-Vertrag.

Wie wird das in einer echten WebSphere-Umgebung deployt? Ueber den JAX-WS- Servlet-Transport - der Anwendungsserver registriert automatisch einen HTTP-Endpunkt fuer die annotierte Klasse (aehnlich wie fuer ein Servlet), sobald sie Teil eines deployten Moduls ist. In dieser Ausbaustufe (vor Phase 4, EAR-Packaging) wird der Endpoint stattdessen in Tests ueber die eingebaute JAX-WS-RI-Mini-HTTP-Laufzeit (javax.xml.ws.Endpoint.publish(...)) veroeffentlicht - funktional dasselbe Prinzip ("ein Java-Objekt wird unter einer URL als SOAP-Endpunkt erreichbar"), nur ohne vollen Container.

Clientseite: @WebServiceRef

Beispiel: BillingIntegrationBean in novaris-policy-core.

@WebServiceRef(wsdlLocation = "wsdl/BillingService.wsdl")
BillingServicePort billingPort;

@WebServiceRef ist das Gegenstueck zu @EJB/@Resource/@PersistenceContext: eine deklarative Injection-Annotation, mit der der Container zur Deployzeit einen fertigen SOAP-Client-Proxy bereitstellt. Der Container liest die referenzierte WSDL, findet das (hier: einzige) <service>/<port>-Paar und generiert daraus eine Instanz von BillingServicePort, die intern alle SOAP-Details (XML-Marshalling, HTTP-Transport, Fault-Deserialisierung) kapselt - der Anwendungscode ruft einfach eine Java-Methode auf.

Ohne Container - der manuelle, dynamische Weg: Ausserhalb eines Containers (z.B. in Tests) uebernimmt niemand die @WebServiceRef-Injection. Der aequivalente manuelle Weg, den BillingServiceEndpointTest und BillingIntegrationBeanRealEndpointTest verwenden:

URL wsdlUrl = new URL(endpointAddress + "?wsdl");
Service service = Service.create(wsdlUrl, BillingServiceLocations.SERVICE_QNAME);
BillingServicePort port = service.getPort(BillingServicePort.class);

Beide Wege fuehren zum selben Ergebnis - ein Objekt, das BillingServicePort implementiert und Aufrufe transparent in SOAP-Requests uebersetzt. @WebServiceRef ist nur die deklarative, containergesteuerte Abkuerzung dafuer.

Timeouts: BindingProvider

Map<String, Object> requestContext = ((BindingProvider) billingPort).getRequestContext();
requestContext.put("javax.xml.ws.client.connectTimeout", 3000);
requestContext.put("javax.xml.ws.client.receiveTimeout", 5000);

Jeder JAX-WS-Client-Proxy implementiert zusaetzlich javax.xml.ws.BindingProvider - darueber lassen sich Laufzeiteinstellungen wie Verbindungs-/Antwort-Timeouts, HTTP-Header oder eine abweichende Endpunkt-Adresse setzen, ohne den generierten/injizierten Proxy selbst zu veraendern. Ohne explizite Timeouts wartet ein SOAP-Aufruf im schlimmsten Fall unbegrenzt auf ein nicht antwortendes Partnersystem - ein haeufig uebersehener Grund fuer haengende Threads in Produktionsproblemen.

Resilienz: Retry und Fehleruebersetzung

BillingIntegrationBean.registerPolicyForBilling(...) zeigt ein einfaches, aber vollstaendiges Resilienzmuster: ein technischer Fehlschlag (javax.xml.ws.WebServiceException - Netzwerkproblem, Timeout, ungueltige SOAP-Antwort) wird einmal automatisch wiederholt, bevor endgueltig aufgegeben und in die projekt- einheitliche LegacySystemUnavailableException (System Exception, siehe docs/10-ejb-konzepte/06-exceptions.md) uebersetzt wird. Ein fachlicher Fehler (BillingRegistrationFault) wird dagegen NICHT wiederholt - ein erneuter Versuch mit denselben Daten wuerde exakt denselben fachlichen Fehler erneut ausloesen, siehe docs/20-soap/03-soap-faults.md.

⌂ Cockpit