Introduksjon til Apache CXF

1. Oversikt

Apache CXF er et JAX-WS-fullstendig rammeverk.

På toppen av funksjoner definert av JAX-WS-standarder, gir Apache CXF muligheten for konvertering mellom WSDL- og Java-klasser, API-er som brukes til å manipulere rå XML-meldinger, støtte for JAX-RS, integrering med Spring Framework, etc.

Denne opplæringen er den første av en serie om Apache CXF, som introduserer grunnleggende egenskaper ved rammeverket. Den bruker bare JAX-WS standard APIer i kildekoden, mens den fremdeles utnytter Apache CXF bak kulissene, for eksempel automatisk genererte WSDL-metadata og CXF-standardkonfigurasjon.

2. Maven-avhengigheter

Nøkkelavhengigheten som trengs for å bruke Apache CXF er org.apache.cxf: cxf - rt - frontend -jaxws. Dette gir en JAX-WS-implementering for å erstatte den innebygde JDK:

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

Legg merke til at denne gjenstanden inneholder en fil som heter javax.xml.ws.spi.Provider inne i META-INF / tjenester katalog. Java VM ser på den første linjen i denne filen for å bestemme JAX-WS-implementeringen du skal bruke. I dette tilfellet er innholdet på linjen org.apache.cxf.jaxws.spi.ProviderImpl, med henvisning til implementeringen levert av Apache CXF.

I denne opplæringen bruker vi ikke en servletbeholder for å publisere tjenesten, og det kreves derfor en annen avhengighet for å gi nødvendige definisjoner av Java-typen:

 org.apache.cxf cxf-rt-transporterer-http-brygge 3.1.6 

For de nyeste versjonene av disse avhengighetene, sjekk ut cxf-rt-frontend-jaxws og cxf-rt-transporterer-http-brygge i Maven sentrale arkiv.

3. Endepunkt for nettjeneste

La oss starte med implementeringsklassen som brukes til å konfigurere tjenestens endepunkt:

@WebService (endpointInterface = "com.baeldung.cxf.introduction.Baeldung") offentlig klasse BaeldungImpl implementerer Baeldung {private Map students = new LinkedHashMap (); public String hei (String name) {return "Hello" + name; } public String helloStudent (Student student) {students.put (students.size () + 1, student); returner "Hei" + student.getName (); } public Map getStudents () {return students; }}

Det viktigste å bli lagt merke til her er tilstedeværelsen av endpointInterface attributt i @Nettjeneste kommentar. Denne attributtet peker på et grensesnitt som definerer en abstrakt kontrakt for nettjenesten.

Alle metodesignaturer som er erklært i endepunktgrensesnittet, må implementeres, men det er ikke nødvendig å implementere grensesnittet.

Her er BaeldungImpl implementeringsklasse implementerer fortsatt følgende endepunktsgrensesnitt for å gjøre det klart at alle deklarerte metodene for grensesnittet er implementert, men å gjøre dette er valgfritt:

@WebService public interface Baeldung {public String hallo (String name); offentlig String halloStudent (studentstudent); @XmlJavaTypeAdapter (StudentMapAdapter.class) offentlig kart getStudents (); }

Som standard bruker Apache CXF JAXB som sin databindende arkitektur. Men siden JAXB ikke direkte støtter binding av en Kart, som returneres fra getStudents metode, vi trenger en adapter for å konvertere Kart til en Java-klasse som JAXB kan bruke.

I tillegg definerer vi for å skille kontraktelementer fra implementeringen Student som et grensesnitt og JAXB støtter heller ikke direkte grensesnitt, derfor trenger vi en adapter til for å håndtere dette. For enkelhets skyld kan vi faktisk erklære Student som en klasse. Bruken av denne typen som grensesnitt er bare en demonstrasjon til for å bruke tilpasningsklasser.

Adapterene er demonstrert i delen rett nedenfor.

4. Egendefinerte adaptere

Denne delen illustrerer måten å bruke tilpasningsklasser til å støtte binding av et Java-grensesnitt og en Kart bruker JAXB.

4.1. Grensesnittadapter

Dette er hvordan Student grensesnitt er definert:

@XmlJavaTypeAdapter (StudentAdapter.class) offentlig grensesnitt Student {public String getName (); }

Dette grensesnittet erklærer bare én metode som returnerer en String og spesifiserer Studentadapter som tilpasningsklasse for å kartlegge seg selv til og fra en type som kan bruke JAXB-binding.

De Studentadapter klasse er definert som følger:

offentlig klasse StudentAdapter utvider XmlAdapter {offentlig StudentImpl-marskalk (Studentstudent) kaster Unntak {hvis (studentinstans av StudentImpl) {retur (StudentImpl) student; } returner ny StudentImpl (student.getName ()); } offentlig Student unmarshal (StudentImpl student) kaster Unntak {retur student; }}

En tilpasningsklasse må implementere XmlAdapter grensesnitt og gi implementering for marskalk og unmarshal metoder. De marskalk metoden transformerer en bundet type (Student, et grensesnitt som JAXB ikke kan håndtere direkte til en verditype (StudentImpl, en konkret klasse som kan behandles av JAXB). De unmarshal metoden gjør ting omvendt.

Her er StudentImpl klassedefinisjon:

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

4.2. Kart Adapter

De getStudents Metode av Baeldung endepunktsgrensesnittet returnerer a Kart og indikerer en tilpasningsklasse for å konvertere Kart til en type som kan håndteres av JAXB. I likhet med Studentadapter klasse, må denne tilpasningsklassen implementere marskalk og unmarshal metoder for XmlAdapter grensesnitt:

offentlig klasse StudentMapAdapter utvider XmlAdapter {public StudentMap marshal (Map boundMap) kaster Unntak {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 (). add (valueEntry); } return valueMap; } public Map unmarshal (StudentMap valueMap) kaster Unntak {Map boundMap = new LinkedHashMap (); for (StudentMap.StudentEntry studentEntry: valueMap.getEntries ()) {boundMap.put (studentEntry.getId (), studentEntry.getStudent ()); } returner boundMap; }}

De StudentMapAdapter klassekart Kart til og fra StudentKart verditype med definisjonen som følger:

@XmlType (name = "StudentMap") offentlig klasse StudentMap {private Listeoppføringer = ny ArrayList (); @XmlElement (nillable = false, name = "entry") offentlig Liste getEntries () {returoppføringer; } @XmlType (name = "StudentEntry") offentlig statisk klasse StudentEntry {private Integer id; privat studentstudent; // getters og setters}}

5. Implementering

5.1. Server Definisjon

For å distribuere webtjenesten diskutert ovenfor, vil vi benytte oss av standard JAX-WS APIer. Siden vi bruker Apache CXF, gjør rammeverket noe ekstra arbeid, f.eks. generere og publisere WSDL-skjemaet. Slik defineres tjenesteserveren:

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

Etter at serveren er aktiv en stund for å lette testing, bør den lukkes for å frigjøre systemressurser. Du kan spesifisere hvilken som helst arbeidstid for serveren basert på dine behov ved å sende en lang argument til Tråd. Sove metode.

5.2. Implementering av Server

I denne opplæringen bruker vi org.codehaus.mojo: exec -maven-plugin plugin for å starte serveren illustrert ovenfor og kontrollere livssyklusen. Dette er erklært i Maven POM-filen som følger:

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

De hovedklasse konfigurasjon refererer til Server klasse der endepunktet for nettjenesten er publisert. Etter å ha kjørt java målet med dette pluginet, kan vi sjekke ut WSDL-skjemaet som automatisk genereres av Apache CXF ved å få tilgang til URL-en // localhost: 8080 / baeldung? wsdl.

6. Test tilfeller

Denne delen veileder deg gjennom trinn for å skrive testtilfeller som brukes til å verifisere webtjenesten vi opprettet før.

Vær oppmerksom på at vi må utføre exec: java mål å starte webserviceserveren før du kjører en test.

6.1. Forberedelse

Det første trinnet er å erklære flere felt for testklassen:

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

Følgende initialiseringsblokk brukes til å starte service felt av javax.xml.ws.Service skriv før du kjører en test:

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

Etter å ha lagt til JUnit-avhengighet i POM-filen, kan vi bruke @Før kommentar som i kodebiten nedenfor. Denne metoden kjører før hver test for å gjenopprette Baeldung Enger:

@Før offentlig ugyldig gjenopprette BaeldungInstances () {baeldungImpl = ny BaeldungImpl (); baeldungProxy = service.getPort (PORT_NAME, Baeldung.class); }

De baeldungProxy variabel er en proxy for endepunktet for nettjenesten, mens baeldungImpl er bare et enkelt Java-objekt. Dette objektet brukes til å sammenligne resultater fra påkallinger av eksterne endepunktmetoder gjennom proxyen med påkallinger av lokale metoder.

Merk at a QName forekomst er identifisert av to deler: en Navnerom URI og en lokal del. Hvis den PORT_NAME argument, av QName type, av Service.getPort Metoden er utelatt, vil Apache CXF anta at argumentets Navnerom URI er pakkenavnet til sluttpunktsgrensesnittet i omvendt rekkefølge, og dets lokale del er grensesnittnavnet lagt til Havn, som er nøyaktig samme verdi av PORT_NAME. Derfor kan vi i denne opplæringen utelate dette argumentet.

6.2. Testimplementering

Den første testsaken vi illustrerer i denne underdelen er å validere svaret som returneres fra en ekstern påkalling av Hallo metode på tjenestens endepunkt:

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

Det er klart at den eksterne endepunktmetoden returnerer samme respons som den lokale metoden, noe som betyr at nettjenesten fungerer som forventet.

Den neste testsaken demonstrerer bruken av heiStudent metode:

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

I dette tilfellet sender klienten a Student motsette seg endepunktet og motta en melding som inneholder studentens navn i retur. I likhet med forrige testtilfelle er svarene fra både eksterne og lokale påkallinger de samme.

Den siste prøvesaken vi viser her er mer komplisert. Som definert av klassen for implementering av tjenestens sluttpunkt, påkaller klienten hver gang heiStudent metoden på endepunktet, den innsendte Student objektet lagres i en cache. Denne hurtigbufferen kan hentes ved å ringe getStudents metode på endepunktet. Følgende testsak bekrefter innholdet i studenter cache representerer det klienten har sendt til nettjenesten:

@Test offentlig ugyldig ved bruk av GetStudentsMethod_thenCorrect () {Student student1 = ny StudentImpl ("Adam"); baeldungProxy.helloStudent (student1); Student student2 = ny StudentImpl ("Eve"); baeldungProxy.helloStudent (student2); Kartlegg studenter = baeldungProxy.getStudents (); assertEquals ("Adam", students.get (1) .getName ()); assertEquals ("Eve", students.get (2) .getName ()); }

7. Konklusjon

Denne opplæringen introduserte Apache CXF, et kraftig rammeverk for å jobbe med webtjenester i Java. Den fokuserte på anvendelsen av rammeverket som en standard JAX-WS-implementering, mens man fortsatt benyttet seg av rammeverkets spesifikke evner i løpetid.

Implementeringen av alle disse eksemplene og kodebiter kan bli funnet i et GitHub-prosjekt.