Inleiding tot Apache CXF

1. Overzicht

Apache CXF is een JAX-WS volledig compatibel framework.

Naast functies die zijn gedefinieerd door JAX-WS-standaarden, biedt Apache CXF de mogelijkheid tot conversie tussen WSDL- en Java-klassen, API's die worden gebruikt om onbewerkte XML-berichten te manipuleren, de ondersteuning voor JAX-RS, integratie met het Spring Framework, enz.

Deze tutorial is de eerste van een serie over Apache CXF, waarin de basiskenmerken van het framework worden geïntroduceerd. Het gebruikt alleen de JAX-WS-standaard-API's in de broncode, terwijl het achter de schermen nog steeds profiteert van Apache CXF, zoals automatisch gegenereerde WSDL-metadata en CXF-standaardconfiguratie.

2. Maven afhankelijkheden

De belangrijkste afhankelijkheid die nodig is om Apache CXF te gebruiken, is org.apache.cxf: cxf - rt - frontend -jaxws. Dit biedt een JAX-WS-implementatie om de ingebouwde JDK-implementatie te vervangen:

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

Merk op dat dit artefact een bestand bevat met de naam javax.xml.ws.spi.Provider binnen in de META-INF / diensten directory. Java VM kijkt naar de eerste regel van dit bestand om de JAX-WS-implementatie te bepalen waarvan gebruik moet worden gemaakt. In dit geval is de inhoud van de regel Org.apache.cxf.jaxws.spi.ProviderImpl, verwijzend naar de implementatie geleverd door Apache CXF.

In deze zelfstudie gebruiken we geen servletcontainer om de service te publiceren, daarom is een andere afhankelijkheid vereist om de benodigde Java-typedefinities te bieden:

 org.apache.cxf cxf-rt-transports-http-steiger 3.1.6 

Raadpleeg voor de nieuwste versies van deze afhankelijkheden cxf-rt-frontend-jaxws en cxf-rt-transports-http-steiger in de centrale opslagplaats van Maven.

3. Webservice-eindpunt

Laten we beginnen met de implementatieklasse die wordt gebruikt om het service-eindpunt te configureren:

@WebService (endpointInterface = "com.baeldung.cxf.introduction.Baeldung") openbare klasse BaeldungImpl implementeert Baeldung {private Map students = new LinkedHashMap (); public String hallo (String naam) {return "Hallo" + naam; } public String helloStudent (Student student) {students.put (students.size () + 1, student); retourneer "Hallo" + student.getName (); } openbare kaart getStudents () {terugkerende studenten; }}

Het belangrijkste dat hier moet worden opgemerkt, is de aanwezigheid van de endpointInterface attribuut in het @WebService annotatie. Dit kenmerk verwijst naar een interface die een abstract contract voor de webservice definieert.

Alle methodehandtekeningen die in de eindpuntinterface zijn gedeclareerd, moeten worden geïmplementeerd, maar het is niet vereist om de interface te implementeren.

Hier de BaeldungImpl implementatieklasse implementeert nog steeds de volgende eindpuntinterface om duidelijk te maken dat alle gedeclareerde methoden van de interface zijn geïmplementeerd, maar dit is optioneel:

@WebService publieke interface Baeldung {public String hallo (String naam); openbare String helloStudent (Student student); @XmlJavaTypeAdapter (StudentMapAdapter.class) openbare kaart getStudents (); }

Apache CXF gebruikt standaard JAXB als zijn gegevensbindingsarchitectuur. Omdat JAXB echter niet direct binding van een Kaart, die wordt geretourneerd door de getStudents methode, we hebben een adapter nodig om de Kaart naar een Java-klasse die JAXB kan gebruiken.

Bovendien definiëren we, om contractelementen te scheiden van hun implementatie Leerling als interface en JAXB ondersteunt ook geen directe interfaces, dus hebben we nog een adapter nodig om hiermee om te gaan. In feite kunnen we voor het gemak aangeven Leerling als een klas. Het gebruik van dit type als interface is nog maar een demonstratie van het gebruik van aanpassingsklassen.

De adapters worden in de sectie rechts hieronder gedemonstreerd.

4. Aangepaste adapters

Deze sectie illustreert de manier om aanpassingsklassen te gebruiken om binding van een Java-interface en een Kaart met behulp van JAXB.

4.1. Interface-adapter

Dit is hoe de Leerling interface is gedefinieerd:

@XmlJavaTypeAdapter (StudentAdapter.class) openbare interface Student {openbare String getName (); }

Deze interface declareert slechts één methode die een Draad en specificeert StudentAdapter als de aanpassingsklasse om zichzelf toe te wijzen aan en van een type dat JAXB-binding kan toepassen.

De StudentAdapter klasse wordt als volgt gedefinieerd:

openbare klasse StudentAdapter breidt XmlAdapter uit {openbare StudentImpl-marshal (student student) gooit uitzondering {if (student instanceof StudentImpl) {return (StudentImpl) student; } retourneer nieuwe StudentImpl (student.getName ()); } openbaar Student unmarshal (StudentImpl student) gooit uitzondering {terugkeer student; }}

Een aanpassingsklasse moet het XmlAdapter interface en bieden implementatie voor de maarschalk en onmarshal methoden. De maarschalk methode transformeert een gebonden type (Leerling, een interface die JAXB niet rechtstreeks kan verwerken) in een waardetype (StudentImpl, een concrete klasse die kan worden verwerkt door JAXB). De onmarshal methode doet het andersom.

Hier is de StudentImpl klasse definitie:

@XmlType (name = "Student") public class StudentImpl implementeert Student {private String name; // constructors, getter en setter}

4.2. Kaart Adapter

De getStudents methode van Baeldung eindpuntinterface retourneert een Kaart en geeft een aanpassingsklasse aan om de Kaart naar een type dat kan worden afgehandeld door JAXB. Net als bij de StudentAdapter klasse moet deze aanpassingsklasse worden geïmplementeerd maarschalk en onmarshal methoden van de XmlAdapter koppel:

openbare klasse StudentMapAdapter breidt XmlAdapter uit {openbare StudentMap-marshal (Map boundMap) gooit uitzondering {StudentMap valueMap = nieuwe StudentMap (); voor (Map.Entry boundEntry: boundMap.entrySet ()) {StudentMap.StudentEntry valueEntry = nieuwe StudentMap.StudentEntry (); valueEntry.setStudent (boundEntry.getValue ()); valueEntry.setId (boundEntry.getKey ()); valueMap.getEntries (). add (valueEntry); } return valueMap; } public Map unmarshal (StudentMap valueMap) gooit uitzondering {Map boundMap = nieuwe LinkedHashMap (); voor (StudentMap.StudentEntry studentEntry: valueMap.getEntries ()) {boundMap.put (studentEntry.getId (), studentEntry.getStudent ()); } return boundMap; }}

De StudentMapAdapter klassenkaarten Kaart van en naar de StudentMap waardetype met de volgende definitie:

@XmlType (name = "StudentMap") openbare klasse StudentMap {privélijstvermeldingen = nieuwe ArrayList (); @XmlElement (nillable = false, name = "entry") openbare lijst getEntries () {retouringangen; } @XmlType (name = "StudentEntry") openbare statische klasse StudentEntry {privé Integer-id; particuliere student student; // getters en setters}}

5. Inzet

5.1. Server Definitie

Om de hierboven besproken webservice te implementeren, zullen we gebruik maken van de standaard JAX-WS API's. Omdat we Apache CXF gebruiken, doet het framework wat extra werk, bijv. genereren en publiceren van het WSDL-schema. Hier is hoe de serviceserver wordt gedefinieerd:

public class Server {public static void main (String args []) gooit InterruptedException {BaeldungImpl implementor = nieuwe BaeldungImpl (); Stringadres = "// localhost: 8080 / baeldung"; Endpoint.publish (adres, implementator); Thread.sleep (60 * 1000); System.exit (0); }}

Nadat de server een tijdje actief is geweest om het testen te vergemakkelijken, moet deze worden afgesloten om systeembronnen vrij te maken. U kunt elke werkduur voor de server specificeren op basis van uw behoeften door een lang argument aan de Draad. Slaap methode.

5.2. Inzet van het Server

In deze tutorial gebruiken we de org.codehaus.mojo: exec -maven-plugin plug-in om de hierboven geïllustreerde server te instantiëren en de levenscyclus ervan te controleren. Dit wordt als volgt in het Maven POM-bestand gedeclareerd:

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

De mainClass configuratie verwijst naar de Server klasse waar het webservice-eindpunt wordt gepubliceerd. Na het uitvoeren van het Java doel van deze plug-in, kunnen we het WSDL-schema bekijken dat automatisch wordt gegenereerd door Apache CXF door de URL te openen // localhost: 8080 / baeldung? wsdl.

6. Testgevallen

Dit gedeelte leidt u door de stappen om testcases te schrijven die worden gebruikt om de webservice te verifiëren die we eerder hebben gemaakt.

Houd er rekening mee dat we het exec: java doel om de webserviceserver te starten voordat u een test uitvoert.

6.1. Voorbereiding

De eerste stap is om verschillende velden voor de testklasse te declareren:

openbare klas StudentTest {privé statische QName SERVICE_NAME = nieuwe QName ("// introductie.cxf.baeldung.com/", "Baeldung"); privé statische QName PORT_NAME = nieuwe QName ("// introductie.cxf.baeldung.com/", "BaeldungPort"); particuliere dienst; particuliere Baeldung baeldungProxy; privé BaeldungImpl baeldungImpl; // andere verklaringen}

Het volgende initialisatieblok wordt gebruikt om het onderhoud veld van de javax.xml.ws.Service typ voordat u een test uitvoert:

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

Na het toevoegen van JUnit-afhankelijkheid aan het POM-bestand, kunnen we de @Voordat annotatie zoals in het onderstaande codefragment. Deze methode wordt vóór elke test uitgevoerd om opnieuw te instantiëren Baeldung velden:

@Before public void reinstantiateBaeldungInstances () {baeldungImpl = nieuwe BaeldungImpl (); baeldungProxy = service.getPort (PORT_NAME, Baeldung.class); }

De baeldungProxy variabele is een proxy voor het webservice-eindpunt, terwijl baeldungImpl is slechts een eenvoudig Java-object. Dit object wordt gebruikt om resultaten van aanroepen van externe eindpuntmethoden via de proxy te vergelijken met aanroepen van lokale methoden.

Merk op dat a QName instantie wordt geïdentificeerd door twee delen: een naamruimte-URI en een lokaal deel. Als het PORT_NAME argument, van de QName type, van de Service.getPort methode is weggelaten, zal Apache CXF aannemen dat de naamruimte-URI van het argument de pakketnaam is van de eindpuntinterface in omgekeerde volgorde en het lokale deel de interfacenaam is toegevoegd door Haven, wat exact dezelfde waarde is als PORT_NAME. Daarom kunnen we in deze tutorial dit argument weglaten.

6.2. Testimplementatie

Het eerste testgeval dat we in deze subsectie illustreren, is het valideren van het antwoord dat wordt geretourneerd door een externe aanroep van de Hallo methode op het service-eindpunt:

@Test openbare leegte whenUsingHelloMethod_thenCorrect () {String endpointResponse = baeldungProxy.hello ("Baeldung"); String localResponse = baeldungImpl.hello ("Baeldung"); assertEquals (localResponse, endpointResponse); }

Het is duidelijk dat de externe eindpuntmethode hetzelfde antwoord retourneert als de lokale methode, wat betekent dat de webservice werkt zoals verwacht.

De volgende testcase toont het gebruik van halloStudent methode:

@Test public void whenUsingHelloStudentMethod_thenCorrect () {Student student = new StudentImpl ("John Doe"); String endpointResponse = baeldungProxy.helloStudent (student); String localResponse = baeldungImpl.helloStudent (student); assertEquals (localResponse, endpointResponse); }

In dit geval dient de klant een Leerling bezwaar maken tegen het eindpunt en ontvangt in ruil daarvoor een bericht met de naam van de leerling. Net als bij de vorige testcase zijn de reacties van zowel externe als lokale aanroepen hetzelfde.

De laatste testcase die we hier laten zien, is ingewikkelder. Zoals gedefinieerd door de implementatieklasse van het service-eindpunt, roept de client elke keer het halloStudent methode op het eindpunt, de verzonden Leerling object wordt opgeslagen in een cache. Deze cache kan worden opgehaald door de getStudents methode op het eindpunt. De volgende testcase bevestigt dat de inhoud van het studenten cache staat voor wat de client naar de webservice heeft gestuurd:

@Test openbare leegte usingGetStudentsMethod_thenCorrect () {Student student1 = nieuwe StudentImpl ("Adam"); baeldungProxy.helloStudent (student1); Student student2 = nieuwe StudentImpl ("Eve"); baeldungProxy.helloStudent (student2); Kaart studenten = baeldungProxy.getStudents (); assertEquals ("Adam", students.get (1) .getName ()); assertEquals ("Eve", students.get (2) .getName ()); }

7. Conclusie

Deze tutorial introduceerde Apache CXF, een krachtig framework om met webservices in Java te werken. Het concentreerde zich op de toepassing van het raamwerk als een standaard JAX-WS-implementatie, terwijl nog steeds gebruik werd gemaakt van de specifieke mogelijkheden van het raamwerk tijdens runtime.

De implementatie van al deze voorbeelden en codefragmenten is te vinden in een GitHub-project.