7

Od WSDL k webovej službe — generovanie servera a klienta pomocou Metro na Java 1...

 2 years ago
source link: https://novotnyr.github.io/scrolls/od-wsdl-k-webovej-sluzbe-metro-pre-java-11/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
Od WSDL k webovej službe — generovanie servera a klienta pomocou Metro na Java 11

Od WSDL k webovej službe — generovanie servera a klienta pomocou Metro na Java 11

2021/10/01

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 na platforme Java 11!

Použijeme na to štandard JAX-WS 2.0 a jeho tradičnú implementáciu Metro.

Java 11 je prvá verzia, ktorá už neobsahuje v základnej knižnici implementáciu JAX-WS 2.0. Zároveň je to posledná verzia, na ktorej referenčná implementácia Metro zatiaľ funguje. Keďže technická podpora tejto Javy končí v roku 2026, dúfajme, že sa to dovtedy podarí zladiť.

Príklad a štruktúra dát

Vybudujme si službu pre objednanie parkovania. V požiadavke pošlem 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

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. na GitHube[https://github.com/novotnyr/kopr-wsdl-server-2021/blob/main/src/main/resources/parking.xsd]

WSDL súbor

Vezmeme si hotový súbor pre WSDL.

Kompletný súbor schémy parking.wsdl nájdeme na konci článku, resp. na GitHube[https://github.com/novotnyr/kopr-wsdl-server-2021/blob/main/src/main/resources/parking.wsdl]

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 11

  • závislosť na knižnici Metro

  • Maven plugin pre generovanie kódu servera

Verzia kompilátora

Dodajme nasledovné projektové vlastnosti:

pom.xml
<properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
</properties>

Závislosť na knižnici Metro

Pridajme si závislosť na knižnici Metro:

pom.xml
<dependency>
    <groupId>com.sun.xml.ws</groupId>
    <artifactId>jaxws-rt</artifactId>
    <version>2.3.3</version>
</dependency>

Maven Plugin

V starších verziách Javy sme mali k dispozícii nástroj wsimport, ktorý však v takejto verzii Metra už nie je dostupný. Pomocou neho sme dokázali vygenerovať kód pre server ale i klienta.

Jeho funkcionalitu nahraďme pluginom pre Maven:

pom.xml
<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jaxws-maven-plugin</artifactId>
    <version>2.6</version>
    <configuration>
        <wsdlDirectory>${project.basedir}/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 CLASSPATH (v koreni). Takto sa na tento súbor odkážeme z bežiaceho servera a vieme ho poskytnúť klientovi.

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

pom.xml
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>sk.upjs.ics.kopr</groupId>
	<artifactId>kopr-parking-server</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>
		<maven.compiler.source>11</maven.compiler.source>
		<maven.compiler.target>11</maven.compiler.target>
	</properties>


	<dependencies>
		<dependency>
			<groupId>com.sun.xml.ws</groupId>
			<artifactId>jaxws-rt</artifactId>
			<version>2.3.3</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>jaxws-maven-plugin</artifactId>
				<version>2.6</version>
				<configuration>
					<wsdlDirectory>${project.basedir}/src/main/resources</wsdlDirectory>
					<wsdlFiles>
						<wsdlFile>parking.wsdl</wsdlFile>
					</wsdlFiles>
					<wsdlLocation>/parking.wsdl</wsdlLocation>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

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 verzie 11 — nie novšej (Metro tam nefunguje) a nie staršej (kompilácia očakáva verziu 11)!

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
      ├── ParkingPortType.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.

V Eclipse je to pravý klik na adresár v strome Package List, a z kontextového menu Build Path | Use as Source Folder.

Implementácia servera

Implementácia servera znamená vytvorenie triedy, ktorá bude implementovať triedu org.example.ParkingPortType.

ParkingServiceImpl.java
package sk.upjs.ics.kopr;

import javax.jws.WebService;
import org.example.parking.ParkingPortType;
import org.example.parking.ParkingRequest;
import org.example.parking.ParkingTicket;

@WebService(endpointInterface = "org.example.parking.ParkingPortType") ()
public class ParkingServiceImpl implements ParkingPortType { ()

    public ParkingTicket getTicket(ParkingRequest part) {
        // implementacia metody
        return new ParkingTicket(); ()
    }
}
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 ParkingPortType sa uvádza na dvoch miestach:

  1. V implements, kde určuje metódy, ktoré v Jave naprogramujeme.

  2. V endpointInterface, kde spárujeme implementáciu s nagenerovaným kontraktom webovej služby.

Ak vynecháme atribút endpointInterface, môže sa stať, že server nageneruje kontrakt a WSDL na základe implementácie — teda „v protismere“ od kódu k WSDL, čo rozhodne nechceme!

Spustenie servera

Server môžeme spustiť jednoducho:

public static void main(String[] args) throws Exception {
    Endpoint.publish("http://localhost:8888/parking", new ParkingServiceImpl());
}

Toto je klasický jednoduchý spôsob, ktorý ale vygeneruje WSDL automaticky na základe zdrojového kódu.

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) throws Exception {
    List<Source> metadata = new ArrayList<Source>(); ()

    var wsdlSource = new StreamSource(ParkingServiceImpl.class.getResourceAsStream("/parking.wsdl")); ()
    wsdlSource.setSystemId("http://www.example.org/parking(parking.wsdl"); ()
    metadata.add(wsdlSource); ()

    var xsdSource = new StreamSource(ParkingServiceImpl.class.getResourceAsStream("/parking.xsd"));()
    xsdSource.setSystemId("http://www.example.org/parking/parking.xsd");()
    metadata.add(xsdSource);()


    var filter = new HashMap<String, Object>();
    filter.put(Endpoint.WSDL_SERVICE, new QName("http://www.example.org/parking", "ParkingServices")); ()
    filter.put(Endpoint.WSDL_PORT, new QName("http://www.example.org/parking", "ParkingService")); ()

    var endpoint = Endpoint.create(new ParkingServiceImpl()); ()
    endpoint.setProperties(filter); ()
    endpoint.setMetadata(metadata);  ()

    endpoint.publish("http://localhost:8888/parking"); ()
}
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.

Menný priestor v kvalifikovanom mene QName sa preberá z atribútu targetNamespace vo WSDL, lokálne meno z atribútu name v elemente wsdl:service, resp. wsdl:port.

Výsledný repozitár

Výsledný repozitár je na GitHube, v repozitári novotnyr/kopr-wsdl-server-2021.

Zdrojáky

XML Schéma

parking.xsd
<?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>
parking.wsdl
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions
        name="parking"

        xmlns:wsdl="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:tns="http://www.example.org/parking"
>

    <wsdl:types>
        <xsd:schema targetNamespace="http://www.example.org/parking">
            <xsd:include schemaLocation="parking.xsd" />
        </xsd:schema>
    </wsdl:types>

    <wsdl:message name="request">
        <wsdl:part name="part" element="tns:parkingRequest" />
    </wsdl:message>

    <wsdl:message name="response">
        <wsdl:part name="part" element="tns:parkingTicket" />
    </wsdl:message>

    <wsdl:portType name="ParkingPortType">
        <wsdl:operation name="getTicket">
            <wsdl:input message="tns:request" />
            <wsdl:output message="tns:response" />
        </wsdl:operation>
    </wsdl:portType>

    <wsdl:binding name="ParkingBinding" type="tns:ParkingPortType">
        <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>

        <wsdl:operation name="getTicket">
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>

    <wsdl:service name="ParkingServices">
        <wsdl:port name="ParkingService" binding="tns:ParkingBinding">
            <soap:address location="http://localhost:8888/parking"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK