SOAP, JAX-WS, Metro a časy v java.time

2022/10/31

Eclipse Metro nevie pracovať s balíčkom java.time.

To je zvláštne, lebo Java 8 je tu od roku 2014, ale vieme to napraviť!

V skutočnosti, s java.time nevie pracovať knižnica JAXB (Jakarta XML Data Binding), ale tá je súčasťou Metra.

Budeme potrebovať:

  • Javu aspoň 11, ale podpora beží veselo na Jave 17 — poslednom LTS v čase písania článku

  • Eclipse Metro pre Jakarta EE 10 (JAX-WS 4.0)

  • Binding customizations Prispôsobenia prevodu XML na objekty a späť, zvané tiež marshalling, či serializácia.

  • knižnicu ThreeTen, ktorá zavedie podporu pre triedy z java.time do JAXB.

  • Maven

  • plugin jaxws-maven-plugin z Metra

Súbor pre Maven

Do pom.xml pridajme závislosti na Metre a ThreeTen:

pom.xml
<dependencies>
    <dependency>
        <groupId>com.sun.xml.ws</groupId>
        <artifactId>jaxws-rt</artifactId>
        <version>4.0.0</version>(1)
    </dependency>
    <dependency>
        <groupId>io.github.threeten-jaxb</groupId>
        <artifactId>threeten-jaxb-core</artifactId>
        <version>2.1.0</version>(2)
    </dependency>
</dependencies>
1 Eclipse Metro pre JAX-WS 4.0 v špecifikácii Jakarta EE 10.
2 Knižnica ThreeTen pre podporu „zabudnutých“ tried v JAXB.

Webservis s dátumami a časmi

Predstavme si webservis, ktorý pracuje s dátumami.

Najprv požiadavka:

<timeRequest xmlns="urn:example:now">anyType</timeRequest>

Odpoveď bude obsahovať aktuálny čas:

<currentLocalDateTime xmlns="urn:example:now">2008-09-29T03:49:45</currentLocalDateTime>

Schéma pre správy

Schéma pokryje požiadavku aj odpoveď:

now.xsd
<schema xmlns="http://www.w3.org/2001/XMLSchema"
        version="1.0"
        targetNamespace="urn:example:now">

    <element name="timeRequest" type="anyType" />
    <element name="currentLocalDateTime" type="dateTime" />
</schema>
Schému dáme v projekte do adresára src/wsdl. Odtiaľ si ju vytiahne mavenovský plugin a tam ju nájde WSDL.

WSDL metadáta

WSDL metadáta budú pracovať s uvedenými správami v jedinej operácii: getNow:

now.wsdl
<definitions
        xmlns="http://schemas.xmlsoap.org/wsdl/"
        xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
        xmlns:tns="urn:example:now"
        targetNamespace="urn:example:now">
    <types>
        <schema xmlns="http://www.w3.org/2001/XMLSchema" version="1.0" targetNamespace="urn:example:now"> (1)
            <include schemaLocation="now.xsd" /> (2)
        </schema>
    </types>
    <message name="getNow">
        <part name="parameters" element="tns:timeRequest"/>
    </message>
    <message name="getNowResponse">
        <part name="parameters" element="tns:currentLocalDateTime"/>
    </message>
    <portType name="TimeService">
        <operation name="getNow"> (3)
            <input message="tns:getNow"/>
            <output message="tns:getNowResponse"/>
        </operation>
    </portType>
    <binding name="binding" type="tns:TimeService">
        <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
        <operation name="getNow">
            <input>
                <soap:body use="literal"/>
            </input>
            <output>
                <soap:body use="literal"/>
            </output>
        </operation>
    </binding>
    <service name="TimeServices">
        <port name="port" binding="tns:binding">
            <soap:address location="http://localhost:18888/ws/now"/>
        </port>
    </service>
</definitions>
1 Schéma pre správy vo formáte XSD.
2 Schému zahrnieme pomocou include, keďže menný priestor WSDL (urn:example:now) a menný priestor schémy sa zhodujú.
3 Máme jedinú operáciu getNow.
Schému dáme v projekte do adresára src/wsdl. Odtiaľ si ju vytiahne mavenovský plugin.

Maven Plugin

Pridajme mavenový plugin, ktorý vygeneruje zdrojáky klienta:

pom.xml
<plugin>
    <groupId>com.sun.xml.ws</groupId>
    <artifactId>jaxws-maven-plugin</artifactId>
    <version>4.0.0</version>
</plugin>

Generovanie zdrojákov

Vygenerujme zdrojáky klienta:

mvn clean jaxws:wsimport compile
Cieľ wsimport hľadá WSDL v adresári src/wsdl.

Uvidíme generované súbory:

target
├── generated-sources
│  └── wsimport
│     └── example
│        └── now
│           ├── ObjectFactory.java
│           ├── TimeService.java
│           └── TimeServices.java

Čo s dátumami?

Trieda TimeService bude pracovať s dátumami typu XMLGregorianCalendar:

TimeService.java
public XMLGregorianCalendar getNow( (1)
    @WebParam(name = "timeRequest", targetNamespace = "urn:example:now", partName = "parameters")
    Object parameters);
1 Metóda vracia XMLGregorianCalendar. To nám nevyhovuje.

XML Bindingy

Dodajme do projektu prispôsobenia mapovania XML na objekty a späť.

Dodáme XML Binding Customization.

Do adresára src/main/jaxws dodáme jaxb-bindings.xml.

V tomto adresári ho odhalí Maven plugin.
src/main/jaxws/jaxb-bindings.xml
<?xml version="1.0" encoding="UTF-8"?>
<bindings version="3.0"
          xmlns="https://jakarta.ee/xml/ns/jaxb"
          xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
          xmlns:xsd="http://www.w3.org/2001/XMLSchema"
          xsi:schemaLocation="https://jakarta.ee/xml/ns/jaxb https://jakarta.ee/xml/ns/jaxb/bindingschema_3_0.xsd"
> (1)
    <globalBindings> (2)
        <xjc:javaType
                xmlType="xsd:dateTime"
                name="java.time.LocalDateTime"
                adapter="io.github.threetenjaxb.core.LocalDateTimeXmlAdapter"
        /> (3)
    </globalBindings>
</bindings>
1 Dôležité sú:
verzia

3.0, i keď používame JAB modernej verzie. Táto verzia platí i pre JAXB 4.0 (v JAX-WS 4.0).

implicitný menný priestor

https://jakarta.ee/xml/ns/jaxb. Pozor, používame menný priestor z projektu Jakarta!

menný priestor pre xjc

tento menný priestor je pre XJC — XML Java Compiler — prekladač XML na anotované Java triedy.

Špeciálne ho použijeme na prispôsobenie prekladu dátumov a časov.

menný priestor pre XML schému

deklarujeme menný priestor pre XML Schema (XSD), z neho vytiahneme dátový typ pre dátumy a časy

2 Deklarujeme pravidlá, ktoré platia globálne, pre všetky triedy.
3 Deklarujeme pravidlo, ktoré:
xmlType

vezme dátový typ z XML — tuto dátum a čas dateTime z XML Schema

name

namapuje ho na dátový typ z Javy

adapter

použije na to adaptér, teda Java kód. Ten použijeme z knižnice ThreeTen-JAXB.

Element javaType je z menného priestoru http://java.sun.com/xml/ns/jaxb/xjc (prefix xjc).

Pozor, existuje totiž aj rovnomenný element z https://jakarta.ee/xml/ns/jaxb (v súbore bez prípony), ten však nepodporuje adaptéry!

Vygenerujme znovu zdrojáky klienta:

mvn clean jaxws:wsimport compile

Uvidíme, že už sa používa java.time.LocalDateTime.

TimeService.java
@XmlJavaTypeAdapter(LocalDateTimeXmlAdapter.class) (2)
@WebMethod
@WebResult(name = "currentLocalDateTime", targetNamespace = "urn:example:now", partName = "parameters")
public LocalDateTime getNow( (1)
    @WebParam(name = "timeRequest", targetNamespace = "urn:example:now", partName = "parameters")
    Object parameters);
1 Používa sa LocalDateTime.
2 Generátor zdrojákov správne použije adaptér, ktorý sme uviedli v XML súbore.

Kód pre klienta

Kód pre klienta následne len využije generované zdrojáky:

public class Client {
    public static void main(String[] args) {
        var serviceLocator = new TimeServices();
        TimeService timeService = serviceLocator.getPort();
        LocalDateTime now = timeService.getNow(new Object());
        System.out.println(now);
    }
}

Zdrojové kódy

>> Home