<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
Úvod
Navrhli sme webovú službu pre SOAP pomocou contract-first? Máme teda kontrakt reprezentovaný súborom WSDL?
Poďme vygenerovať kód pre server SOAP na kombinácii:
-
Java 17
-
špecifikáciu Jakarta XML Web Services 4.0 (nástupca JAX-WS)
-
referenčnú implementáciu Eclipse Metro.
-
plugin pre Maven z knižnice Eclipse Metro.
Príklad a štruktúra dát
Vybudujme si službu pre objednanie parkovania. V požiadavke pošleme EČV vozidla a parkovaciu zónu a v odpovedi získame identifikátor parkovacieho lístka a dátum platnosti.
Pripravíme si:
-
schému pre správy v tvare XSD (XML Schema)
-
popisný súbor WSDL
-
mavenovský projekt s
pom.xml
pre serverovskú časť.
XML schéma
Vezmeme schému pre správy pomocou XML Schemy.
Kompletný súbor schémy parking.xsd nájdeme na konci článku, resp. v repozitári na GitHube.
|
WSDL súbor
Vezmeme si hotový súbor pre WSDL.
Kompletný súbor schémy parking.wsdl nájdeme na konci článku, resp. v repozitári na GitHube.
|
Projekt pre serverovskú časť
Založme si nový projekt založený na buildovacom nástroji Maven.
V pom.xml
definujeme:
-
verziu kódu pre kompilátor: použijeme Javu 17
-
závislosť na knižnici Eclipse Metro
-
Maven plugin pre generovanie kódu servera
Verzia kompilátora
Dodajme nasledovné projektové vlastnosti:
Závislosť na knižnici Metro
Pridajme si závislosť na knižnici Metro:
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
<version>4.0.0</version>
</dependency>
Maven Plugin
Generovanie kostry serverovskej časti projektu, teda generovanie zdrojových kódov podľa dodaného WSDL súboru uskutočníme cez mavenovský plugin.
<plugin>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<version>4.0.0</version>
<configuration>
<wsdlDirectory>src/main/resources</wsdlDirectory>(2)
<wsdlFiles>
<wsdlFile>parking.wsdl</wsdlFile>(1)
</wsdlFiles>
<wsdlLocation>/parking.wsdl</wsdlLocation>(3)
</configuration>
</plugin>
1 | Definujme odkaz na názov WSDL, z ktorého vygenerujeme kód. |
2 | Uvedieme cestu k adresáru v projekte, z ktorého vytiahneme WSDL súbor. |
3 | Aby sa v generovanom kóde zbytočne neobjavovali celé cesty k lokálnemu súborovému systému používateľa, uveďme explicitnú adresu ku kódu. |
Ak umiestnime WSDL súbor medzi prostriedky (resources), objaví sa vo výslednom JAR archíve a teda v ceste |
Lokácia wsdlLocation s lomkou na začiatku znamená, že WSDL súbor pri budovaní klientskeho kódu sa vytiahne z cesty CLASSPATH .
|
Celý pom.xml
Celý súbor pom.xml nájdeme na na GitHube.
|
Rozmiestnenie súborov
Oba súbory — parking.wsdl
aj parking.xsd
umiestnime do src/main/resources
, pretože tak sme to nastavili v pom.xml
v adresári wsdlDirectory
.
Zostavenie projektu
Zdrojáky pre server nagenerujeme nasledovne:
mvn jaxws:wsimport package
Ak spúšťame Maven zo shellu, dajme si pozor, aby Java, v ktorej sa spúšťal Maven, bola vo verzii 17. |
Zdrojáky sa nagenerujú do priečinka:
target/generated-sources/wsimport/
Pre naše WSDL sa vygeneruje nasledovná štruktúra:
org
└── example
└── parking
├── ObjectFactory.java
├── package-info.java
├── ParkingService.java
├── ParkingRequest.java
├── ParkingServices.java
└── ParkingTicket.java
Názvy priečinkov / balíčkov sa odvodia z targetNamespace vo WSDL či schéme.
|
Konfigurácia projektu
Adresár target/generated-sources/wsimport/
je užitočné potrebné pridať do projektu ako miesto so zdrojovými kódmi.
|
Implementácia servera
Implementácia servera znamená vytvorenie triedy, ktorá bude implementovať triedu org.example.ParkingService
.
package com.github.novotnyr.soap;
import jakarta.jws.WebService;
import org.example.parking.ParkingRequest;
import org.example.parking.ParkingService;
import org.example.parking.ParkingTicket;
@WebService(endpointInterface = "org.example.parking.ParkingService") (2)
public class DefaultParkingService implements ParkingService { (1)
@Override
public ParkingTicket getTicket(ParkingRequest part) {
return new ParkingTicket(); (3)
}
}
1 | Implementujeme interfejs, ktorý vznikol generovaním kódu. |
2 | V atribúte endpointInterface uvedieme interfejs s kontraktom webovej služby pre JAX-WS. |
3 | Pripravíme implementáciu metódy — v tomto prípade veľmi jednoduchú. |
Interfejs
Ak vynecháme atribút |
Spustenie servera
Server môžeme spustiť jednoducho:
package com.github.novotnyr.soap;
import jakarta.xml.ws.Endpoint;
public class ParkingSoapServer {
public static void main(String[] args) {
Endpoint.publish("http://localhost:8888/parking", new DefaultParkingService());
}
}
Server beží na porte 8888 a spĺňa špecifikáciu WSDL.
Táto služba zverejňuje WSDL na adrese http://localhost:8888/parking?wsdl .
Toto WSDL je však autogenerované, čo popiera zmysel ručnej tvorby.
|
Spustenie servera s naším WSDL
Ak chceme použiť existujúce WSDL a to zverejniť klientovi, musíme prispôsobiť nasadenie služby.
Budeme predpokladať, že v CLASSPATH máme aj WSDL (parking.wsdl
) aj XSD (parking.xsd
).
public static void main(String[] args) {
List<Source> metadata = new ArrayList<Source>(); (1)
var wsdlSource = new StreamSource(DefaultParkingService.class.getResourceAsStream("/parking.wsdl")); (2)
wsdlSource.setSystemId("http://www.example.org/parking/parking.wsdl"); (3)
metadata.add(wsdlSource); (4)
var xsdSource = new StreamSource(DefaultParkingService.class.getResourceAsStream("/parking.xsd"));(2)
xsdSource.setSystemId("http://www.example.org/parking/parking.xsd");(3)
metadata.add(xsdSource);(4)
var filter = new HashMap<String, Object>();
filter.put(Endpoint.WSDL_SERVICE, new QName("http://www.example.org/parking", "ParkingServices")); (5)
filter.put(Endpoint.WSDL_PORT, new QName("http://www.example.org/parking", "ParkingService")); (6)
var endpoint = Endpoint.create(new DefaultParkingService()); (7)
endpoint.setProperties(filter); (8)
endpoint.setMetadata(metadata); (9)
endpoint.publish("http://localhost:8888/parking"); (10)
}
1 | Musíme si pripraviť zoznam pre metadáta: teda WSDL a XSD. |
2 | Vytvoríme objekt Source reprezentujúci XML súbor pre WSDL či schému.
Tento objekt načítame z CLASSPATH : to je reprezentované lomkou v argumente getResourceAsStream . |
3 | Každý takýto objekt Source potrebuje jednoznačný identifikátor v tvare URL s použitím protokolu HTTP alebo file .
Keďže na konkrétnej hodnote nezáleží, vytvoríme si vymyslený ukážkový identifikátor. |
4 | Súbor dodáme do metadát. |
5 | Pomocou properties definujeme filter na službu service a port z WSDL, na ktorý použijeme naše metadáta, teda na ktorom zmeníme WSDL a XML schému XSD.
Pomocou WSDL_SERVICE určíme kvalifikované meno (menný priestor a názov elementu) pre element wsdl:service z WSDL. |
6 | Pomocou WSDL_PORT určíme kvalifikované meno (menný priestor a názov elementu) pre element wsdl:port z WSDL. |
7 | Vytvoríme nový endpoint nad našou triedou s implementáciou servera. |
8 | Nastavíme filter cez properties. |
9 | Nastavíme nové metadáta služby. |
10 | Endpoint vypublikujeme na danej adrese. |
Backend teraz môžeme spustiť ako Java aplikáciu!
Na adrese http://localhost:8888/parking?wsdl uvidíme našej ručne písané WSDL!
|
Pri deklarovaní filtra (properties) sa musia kvalifikované názvy presne zhodovať s názvami vo WSDL.
|
Výsledný repozitár
Výsledný repozitár je na GitHube, v repozitári novotnyr/jaxws-wsdl-server-2022.
Zdrojáky
XML Schéma
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.org/parking"
elementFormDefault="qualified">
<element name="parkingRequest">
<complexType>
<sequence>
<element name="carId" type="string" />
<element name="zone" type="int" />
</sequence>
</complexType>
</element>
<element name="parkingTicket">
<complexType>
<sequence>
<element name="id" type="string" />
<element name="validUntil" type="dateTime" />
</sequence>
</complexType>
</element>
</schema>
WSDL
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<definitions
name="parking"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
targetNamespace="http://www.example.org/parking"
xmlns:p="http://www.example.org/parking"
>
<types>
<xsd:schema targetNamespace="http://www.example.org/parking">
<xsd:include schemaLocation="parking.xsd" />
</xsd:schema>
</types>
<message name="parkingRequest">
<part name="part" element="p:parkingRequest" />
</message>
<message name="parkingResponse">
<part name="part" element="p:parkingTicket" />
</message>
<portType name="ParkingPortType">
<operation name="getTicket">
<input message="p:parkingRequest" />
<output message="p:parkingResponse" />
</operation>
</portType>
<binding name="ParkingBinding" type="p:ParkingPortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="getTicket">
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="ParkingServices">
<port name="ParkingService" binding="p:ParkingBinding">
<soap:address location="http://localhost:8888/parking"/>
</port>
</service>
</definitions>
Deskriptor pre Maven
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.novotnyr</groupId>
<artifactId>jaxws-wsdl-server</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.glassfish.metro</groupId>
<artifactId>webservices-rt</artifactId>
<version>4.0.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<version>4.0.0</version>
<configuration>
<wsdlDirectory>src/main/resources</wsdlDirectory>
<wsdlFiles>
<wsdlFile>parking.wsdl</wsdlFile>
</wsdlFiles>
<wsdlLocation>/parking.wsdl</wsdlLocation>
</configuration>
</plugin>
</plugins>
</build>
</project>