Introduktion til Apache CXF

1. Oversigt

Apache CXF er en JAX-WS fuldstændig kompatibel ramme.

Ud over funktioner defineret af JAX-WS-standarder giver Apache CXF mulighed for konvertering mellem WSDL- og Java-klasser, API'er, der bruges til at manipulere rå XML-meddelelser, understøttelse af JAX-RS, integration med Spring Framework osv.

Denne vejledning er den første af en serie om Apache CXF, der introducerer grundlæggende egenskaber ved rammen. Det bruger kun JAX-WS-standard-API'erne i kildekoden, mens det stadig drager fordel af Apache CXF bag kulisserne, såsom automatisk genererede WSDL-metadata og CXF-standardkonfiguration.

2. Maven-afhængigheder

Den nøgleafhængighed, der er nødvendig for at bruge Apache CXF, er org.apache.cxf: cxf - rt - frontend -jaxws. Dette giver en JAX-WS-implementering til erstatning af den indbyggede JDK:

 org.apache.cxf cxf-rt-frontend-jaxws 3.1.6 

Bemærk, at denne artefakt indeholder en fil med navnet javax.xml.ws.spi.Provider inde i META-INF / tjenester vejviser. Java VM ser på den første linje i denne fil for at bestemme JAX-WS-implementeringen, der skal bruges. I dette tilfælde er linjens indhold org.apache.cxf.jaxws.spi.ProviderImpl, der henviser til implementeringen leveret af Apache CXF.

I denne vejledning bruger vi ikke en servletcontainer til at offentliggøre tjenesten, derfor kræves der en anden afhængighed for at give nødvendige Java-definitioner:

 org.apache.cxf cxf-rt-transporterer-http-anløbsbro 3.1.6 

Se de seneste versioner af disse afhængigheder cxf-rt-frontend-jaxws og cxf-rt-transporterer-http-anløbsbro i Mavens centrale arkiv.

3. Webtjenestens slutpunkt

Lad os starte med implementeringsklassen, der bruges til at konfigurere tjenesteendepunktet:

@WebService (endpointInterface = "com.baeldung.cxf.introduction.Baeldung") offentlig klasse BaeldungImpl implementerer Baeldung {private Map studerende = ny LinkedHashMap (); offentlig String hej (String name) {return "Hello" + name; } offentlig streng hejStudent (studerende) {students.put (students.size () + 1, student); returner "Hej" + student.getName (); } offentlige kort getStudents () {returnere studerende; }}

Det vigtigste, der skal bemærkes her, er tilstedeværelsen af endpointInterface attribut i @WebService kommentar. Denne attribut peger på en grænseflade, der definerer en abstrakt kontrakt for webservicen.

Alle metodesignaturer, der er angivet i slutpunktsgrænsefladen, skal implementeres, men det er ikke nødvendigt at implementere grænsefladen.

Her er BaeldungImpl implementeringsklasse implementerer stadig følgende slutpunktsgrænseflade for at gøre det klart, at alle de erklærede metoder til grænsefladen er implementeret, men det er valgfrit at gøre dette:

@WebService offentlig grænseflade Baeldung {public String hej (String name); offentlig String helloStudent (studerende); @XmlJavaTypeAdapter (StudentMapAdapter.class) public Map getStudents (); }

Som standard bruger Apache CXF JAXB som sin databindende arkitektur. Men da JAXB ikke direkte understøtter binding af en Kort, som returneres fra getStudents metode, vi har brug for en adapter til at konvertere Kort til en Java-klasse, som JAXB kan bruge.

Derudover definerer vi for at adskille kontraktelementer fra deres implementering Studerende som en grænseflade, og JAXB understøtter heller ikke direkte grænseflader, og derfor har vi brug for endnu en adapter til at håndtere dette. For nemheds skyld kan vi faktisk erklære Studerende som en klasse. Brugen af ​​denne type som en grænseflade er blot endnu en demonstration af brugen af ​​tilpasningsklasser.

Adaptere vises i afsnittet lige nedenfor.

4. Brugerdefinerede adaptere

Dette afsnit illustrerer måden at bruge tilpasningsklasser til at understøtte binding af en Java-grænseflade og en Kort ved hjælp af JAXB.

4.1. Interface adapter

Dette er, hvordan Studerende interface er defineret:

@XmlJavaTypeAdapter (StudentAdapter.class) offentlig grænseflade Student {public String getName (); }

Denne grænseflade erklærer kun en metode, der returnerer en Snor og specificerer Studentadapter som tilpasningsklasse for at kortlægge sig selv til og fra en type, der kan anvende JAXB-binding.

Det Studentadapter klasse defineres som følger:

public class StudentAdapter udvider XmlAdapter {public StudentImpl marshal (Student student) kaster Undtagelse {if (student instanceof StudentImpl) {return (StudentImpl) student; } returner ny StudentImpl (student.getName ()); } offentlig Student unmarshal (StudentImpl student) kaster Undtagelse {returstuderende; }}

En tilpasningsklasse skal implementere Xml-adapter interface og give implementering til marskal og unmarshal metoder. Det marskal metode omdanner en bundet type (Studerende, en grænseflade, som JAXB ikke kan håndtere direkte til en værditype (StudentImpl, en konkret klasse, der kan behandles af JAXB). Det unmarshal metode gør tingene omvendt.

Her er StudentImpl klasse definition:

@XmlType (name = "Student") offentlig klasse StudentImpl implementerer Student {privat strengnavn; // konstruktører, getter og setter}

4.2. Kort Adapter

Det getStudents metode til Baeldung slutpunktsgrænseflade returnerer a Kort og angiver en tilpasningsklasse til at konvertere Kort til en type, der kan håndteres af JAXB. Svarende til Studentadapter klasse, skal denne tilpasningsklasse implementeres marskal og unmarshal metoder til Xml-adapter grænseflade:

public class StudentMapAdapter udvider XmlAdapter {public StudentMap marshal (Map boundMap) kaster Exception {StudentMap valueMap = new StudentMap (); for (Map.Entry boundEntry: boundMap.entrySet ()) {StudentMap.StudentEntry valueEntry = new StudentMap.StudentEntry (); valueEntry.setStudent (boundEntry.getValue ()); valueEntry.setId (boundEntry.getKey ()); valueMap.getEntries (). tilføj (valueEntry); } return valueMap; } public Map unmarshal (StudentMap valueMap) kaster Undtagelse {Map boundMap = new LinkedHashMap (); for (StudentMap.StudentEntry studentEntry: valueMap.getEntries ()) {boundMap.put (studentEntry.getId (), studentEntry.getStudent ()); } returner boundMap; }}

Det StudentMapAdapter klassekort Kort til og fra StudentMap værditype med definitionen som følger:

@XmlType (name = "StudentMap") offentlig klasse StudentMap {private Listeindgange = ny ArrayList (); @XmlElement (nillable = false, name = "entry") offentlig liste getEntries () {return poster; } @XmlType (name = "StudentEntry") offentlig statisk klasse StudentEntry {privat heltal-id; privat studerende studerende; // getters og setters}}

5. Implementering

5.1. Server Definition

For at implementere den webtjeneste, der er diskuteret ovenfor, bruger vi standard JAX-WS API'erne. Da vi bruger Apache CXF, gør rammen noget ekstra arbejde, f.eks. generering og udgivelse af WSDL-skemaet. Sådan defineres serviceserveren:

public class Server {public static void main (String args []) kaster InterruptedException {BaeldungImpl implementor = new BaeldungImpl (); String address = "// localhost: 8080 / baeldung"; Endpoint.publish (adresse, implementer); Tråd. Søvn (60 * 1000); System.exit (0); }}

Når serveren er aktiv et stykke tid for at lette test, skal den lukkes for at frigøre systemressourcer. Du kan specificere enhver arbejdstid for serveren baseret på dine behov ved at videresende en lang argument til Tråd. Sove metode.

5.2. Implementering af Server

I denne vejledning bruger vi org.codehaus.mojo: exec -maven-plugin plugin for at instantiere serveren illustreret ovenfor og kontrollere dens livscyklus. Dette erklæres i Maven POM-filen som følger:

 org.codehaus.mojo exec-maven-plugin com.baeldung.cxf.introduction.Server 

Det hovedklasse konfiguration henviser til Server klasse, hvor webservicens slutpunkt er offentliggjort. Efter at have kørt java mål med dette plugin, kan vi tjekke WSDL-skemaet, der automatisk genereres af Apache CXF ved at få adgang til URL'en // localhost: 8080 / baeldung? wsdl.

6. Test tilfælde

Dette afsnit gennemgår trin til at skrive testsager, der bruges til at verificere den webservice, vi oprettede før.

Bemærk, at vi skal udføre exec: java mål at starte webserviceserveren, før du kører en test.

6.1. Forberedelse

Det første trin er at erklære flere felter til testklassen:

offentlig klasse StudentTest {privat statisk QName SERVICE_NAME = nyt QName ("// introduktion.cxf.baeldung.com/", "Baeldung"); privat statisk QName PORT_NAME = nyt QName ("// introduktion.cxf.baeldung.com/", "BaeldungPort"); privat service; privat Baeldung baeldungProxy; privat BaeldungImpl baeldungImpl; // andre erklæringer}

Den følgende initialiseringsblok bruges til at starte service felt i javax.xml.ws.Service type inden du kører en test:

{service = Service.create (SERVICE_NAME); String endpointAddress = "// localhost: 8080 / baeldung"; service.addPort (PORT_NAME, SOAPBinding.SOAP11HTTP_BINDING, endpointAddress); }

Efter at have tilføjet JUnit-afhængighed til POM-filen kan vi bruge @Før kommentar som i kodestykket nedenfor. Denne metode kører før hver test for at genoptage Baeldung felter:

@Før offentligt ugyldigt genindføreBaeldungInstances () {baeldungImpl = ny BaeldungImpl (); baeldungProxy = service.getPort (PORT_NAME, Baeldung.class); }

Det baeldungProxy variabel er en proxy for webservicens slutpunkt, mens baeldungImpl er bare et simpelt Java-objekt. Dette objekt bruges til at sammenligne resultater af påkald af eksterne slutpunktsmetoder gennem proxyen med påkald af lokale metoder.

Bemærk, at en QName forekomst identificeres af to dele: en Navnerum URI og en lokal del. Hvis den PORT_NAME argument af QName type, af Service.getPort metode udelades, vil Apache CXF antage, at argumentets Navneområde URI er pakkenavnet på slutpunktsgrænsefladen i omvendt rækkefølge, og dens lokale del er grænsefladenavnet tilføjet af Havn, som er nøjagtig den samme værdi af PORT_NAME. Derfor kan vi i denne vejledning udelade dette argument.

6.2. Testimplementering

Den første testtilfælde, vi illustrerer i dette underafsnit, er at validere svaret, der returneres fra en ekstern påkaldelse af Hej metode på tjenesteendepunktet:

@Test offentlig ugyldig nårUsingHelloMethod_thenCorrect () {String endpointResponse = baeldungProxy.hello ("Baeldung"); String localResponse = baeldungImpl.hello ("Baeldung"); assertEquals (localResponse, endpointResponse); }

Det er klart, at fjernendepunktmetoden returnerer det samme svar som den lokale metode, hvilket betyder, at webservicen fungerer som forventet.

Den næste testsag demonstrerer brugen af hejStudent metode:

@Test offentlig ugyldig nårUsingHelloStudentMethod_thenCorrect () {Studentstudent = ny StudentImpl ("John Doe"); String endpointResponse = baeldungProxy.helloStudent (student); String localResponse = baeldungImpl.helloStudent (studerende); assertEquals (localResponse, endpointResponse); }

I dette tilfælde indsender klienten a Studerende modsætter sig slutpunktet og modtager til gengæld en besked, der indeholder elevens navn. Ligesom den forrige testsag er svarene fra både fjernbetjening og lokal påkaldelse de samme.

Den sidste testsag, som vi viser herover, er mere kompliceret. Som defineret af implementeringsklassen for serviceendepunkter, hver gang klienten påberåber sig hejStudent metode på slutpunktet, den indsendte Studerende objekt gemmes i en cache. Denne cache kan hentes ved at ringe til getStudents metode på slutpunktet. Følgende testsag bekræfter, at indholdet af studerende cache repræsenterer det, klienten har sendt til webservicen:

@Test offentligt ugyldigt ved hjælp af GetStudentsMethod_thenCorrect () {Student student1 = ny StudentImpl ("Adam"); baeldungProxy.helloStudent (student1); Studerende student2 = ny StudentImpl ("Eve"); baeldungProxy.helloStudent (student2); Kortstuderende = baeldungProxy.getStudents (); assertEquals ("Adam", students.get (1) .getName ()); assertEquals ("Eve", students.get (2) .getName ()); }

7. Konklusion

Denne tutorial introducerede Apache CXF, en stærk ramme til at arbejde med webservices i Java. Det fokuserede på anvendelsen af ​​rammen som en standard JAX-WS-implementering, mens den stadig benyttede sig af rammens specifikke muligheder under kørsel.

Implementeringen af ​​alle disse eksempler og kodestykker kan findes i et GitHub-projekt.