Pengenalan kepada Apache CXF

1. Gambaran keseluruhan

Apache CXF adalah rangka kerja yang mematuhi sepenuhnya JAX-WS.

Di atas ciri yang ditentukan oleh piawaian JAX-WS, Apache CXF menyediakan kemampuan penukaran antara kelas WSDL dan Java, API yang digunakan untuk memanipulasi mesej XML mentah, sokongan untuk JAX-RS, penyatuan dengan Spring Framework, dll.

Tutorial ini adalah yang pertama dari siri Apache CXF, memperkenalkan ciri asas kerangka. Ia hanya menggunakan API standard JAX-WS dalam kod sumber sementara masih memanfaatkan Apache CXF di belakang layar, seperti metadata WSDL yang dihasilkan secara automatik dan konfigurasi lalai CXF.

2. Pergantungan Maven

Ketergantungan utama yang diperlukan untuk menggunakan Apache CXF adalah org.apache.cxf: cxf - rt - frontend - jaxws . Ini memberikan pelaksanaan JAX-WS untuk menggantikan JDK terbina dalam:

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

Perhatikan bahawa artifak ini mengandungi fail bernama javax.xml.ws.spi.Penyedia di dalam direktori META-INF / perkhidmatan . Java VM melihat baris pertama file ini untuk menentukan pelaksanaan JAX-WS yang akan digunakan. Dalam kes ini, kandungan baris adalah o rg.apache.cxf.jaxws.spi.ProviderImpl , merujuk kepada pelaksanaan yang disediakan oleh Apache CXF.

Dalam tutorial ini, kami tidak menggunakan wadah servlet untuk menerbitkan perkhidmatan, oleh itu diperlukan satu lagi kebergantungan untuk memberikan definisi jenis Java yang diperlukan:

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

Untuk versi terbaru kebergantungan ini, sila periksa cxf-rt-frontend-jaxws dan cxf-rt-transports-http-jetty di repositori pusat Maven.

3. Titik Akhir Perkhidmatan Web

Mari kita mulakan dengan kelas pelaksanaan yang digunakan untuk mengkonfigurasi titik akhir perkhidmatan:

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

Perkara paling penting yang perlu diperhatikan di sini adalah kehadiran atribut endpointInterface dalam anotasi @WebService . Atribut ini menunjukkan antara muka yang menentukan kontrak abstrak untuk perkhidmatan web.

Semua tandatangan kaedah yang dinyatakan di antara muka titik akhir perlu dilaksanakan, tetapi tidak diperlukan untuk melaksanakan antara muka.

Di sini kelas pelaksanaan BaeldungImpl masih menerapkan antara muka titik akhir berikut untuk menjelaskan bahawa semua kaedah antara muka yang dinyatakan telah dilaksanakan, tetapi melakukan ini adalah pilihan:

@WebService public interface Baeldung { public String hello(String name); public String helloStudent(Student student); @XmlJavaTypeAdapter(StudentMapAdapter.class) public Map getStudents(); }

Secara lalai, Apache CXF menggunakan JAXB sebagai seni bina pengikatan datanya. Namun, kerana JAXB tidak secara langsung mendukung pengikatan Peta , yang dikembalikan dari metode getStudents , kami memerlukan penyesuai untuk mengubah Peta menjadi kelas Java yang dapat digunakan JAXB .

Sebagai tambahan, untuk memisahkan elemen kontrak dari pelaksanaannya, kami mendefinisikan Pelajar sebagai antara muka dan JAXB juga tidak menyokong antara muka secara langsung, oleh itu kami memerlukan satu lagi penyesuai untuk menangani perkara ini. Sebenarnya, untuk kemudahan, kami boleh mengisytiharkan Pelajar sebagai kelas. Penggunaan jenis ini sebagai antara muka adalah satu lagi demonstrasi penggunaan kelas penyesuaian.

Adaptor ditunjukkan dalam bahagian di bawah.

4. Adaptor Tersuai

Bahagian ini menggambarkan cara menggunakan kelas penyesuaian untuk menyokong pengikatan antara muka Java dan Peta menggunakan JAXB.

4.1. Penyesuai antara muka

Ini adalah bagaimana antara muka Pelajar ditakrifkan:

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

Antaramuka ini hanya menyatakan satu kaedah mengembalikan String dan menentukan StudentAdapter sebagai kelas penyesuaian untuk memetakan dirinya ke dan dari jenis yang dapat menerapkan pengikatan JAXB.

The StudentAdapter kelas ditakrifkan seperti berikut:

public class StudentAdapter extends XmlAdapter { public StudentImpl marshal(Student student) throws Exception { if (student instanceof StudentImpl) { return (StudentImpl) student; } return new StudentImpl(student.getName()); } public Student unmarshal(StudentImpl student) throws Exception { return student; } }

Kelas penyesuaian mesti melaksanakan antara muka XmlAdapter dan menyediakan pelaksanaan untuk kaedah marshal dan unmarshal . The marshal kaedah mengubah jenis terikat ( Pelajar , antara muka yang JAXB tidak boleh langsung mengendalikan) ke dalam nilai jenis ( StudentImpl , kelas konkrit yang boleh diproses oleh JAXB). The unmarshal kaedah melakukan sesuatu dengan cara sekitar yang lain.

Berikut adalah definisi kelas StudentImpl :

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

4.2. Penyesuai Peta

The getStudents kaedah Baeldung muka titik akhir mengembalikan Peta dan menunjukkan kelas penyesuaian untuk menukar Peta untuk jenis yang boleh dikendalikan oleh JAXB. Sama dengan kelas StudentAdapter , kelas adaptasi ini mesti melaksanakan kaedah marshal dan unmarshal pada antara muka XmlAdapter :

public class StudentMapAdapter extends XmlAdapter
    
      { public StudentMap marshal(Map boundMap) throws 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().add(valueEntry); } return valueMap; } public Map unmarshal(StudentMap valueMap) throws Exception { Map boundMap = new LinkedHashMap(); for (StudentMap.StudentEntry studentEntry : valueMap.getEntries()) { boundMap.put(studentEntry.getId(), studentEntry.getStudent()); } return boundMap; } }
    

The StudentMapAdapter kelas memetakan Peta ke dan dari StudentMap jenis nilai dengan takrif yang berikut:

@XmlType(name = "StudentMap") public class StudentMap { private List entries = new ArrayList(); @XmlElement(nillable = false, name = "entry") public List getEntries() { return entries; } @XmlType(name = "StudentEntry") public static class StudentEntry { private Integer id; private Student student; // getters and setters } }

5. Penyebaran

5.1. Definisi Pelayan

Untuk menggunakan perkhidmatan web yang dibincangkan di atas, kami akan menggunakan API JAX-WS standard. Oleh kerana kami menggunakan Apache CXF, kerangka kerja tersebut memerlukan beberapa pekerjaan tambahan, misalnya menghasilkan dan menerbitkan skema WSDL. Inilah cara pelayan perkhidmatan ditakrifkan:

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

Setelah pelayan aktif sebentar untuk memudahkan pengujian, server harus ditutup untuk melepaskan sumber sistem. Anda boleh menentukan jangka masa kerja untuk pelayan berdasarkan keperluan anda dengan menyampaikan argumen panjang ke kaedah Thread.sleep .

5.2. Penyebaran Pelayan

Dalam tutorial ini, kami menggunakan plugin org.codehaus.mojo: exec -maven- plugin untuk mewujudkan pelayan yang digambarkan di atas dan mengawal kitaran hayatnya. Ini dinyatakan dalam fail POM Maven seperti berikut:

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

The mainClass konfigurasi merujuk kepada Server kelas di mana titik akhir perkhidmatan web diterbitkan. Setelah menjalankan tujuan java plugin ini, kita dapat melihat skema WSDL yang dihasilkan secara automatik oleh Apache CXF dengan mengakses URL // localhost: 8080 / baeldung? Wsdl .

6. Kes Ujian

Bahagian ini memandu anda melalui langkah-langkah untuk menulis kes ujian yang digunakan untuk mengesahkan perkhidmatan web yang kami buat sebelumnya.

Harap maklum bahawa kita perlu melaksanakan tujuan exec: java untuk memulakan pelayan perkhidmatan web sebelum menjalankan sebarang ujian.

6.1. Penyediaan

Langkah pertama adalah menyatakan beberapa bidang untuk kelas ujian:

public class StudentTest { private static QName SERVICE_NAME = new QName("//introduction.cxf.baeldung.com/", "Baeldung"); private static QName PORT_NAME = new QName("//introduction.cxf.baeldung.com/", "BaeldungPort"); private Service service; private Baeldung baeldungProxy; private BaeldungImpl baeldungImpl; // other declarations }

The following initializer block is used to initiate the service field of the javax.xml.ws.Service type prior to running any test:

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

After adding JUnit dependency to the POM file, we can use the @Before annotation as in the code snippet below. This method runs before every test to re-instantiate Baeldung fields:

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

The baeldungProxy variable is a proxy for the web service endpoint, while baeldungImpl is just a simple Java object. This object is used to compare results of invocations of remote endpoint methods through the proxy with invocations of local methods.

Note that a QName instance is identified by two parts: a Namespace URI and a local part. If the PORT_NAME argument, of the QName type, of the Service.getPort method is omitted, Apache CXF will assume that argument's Namespace URI is the package name of the endpoint interface in the reverse order and its local part is the interface name appended by Port, which is the exact same value of PORT_NAME. Therefore, in this tutorial we may leave this argument out.

6.2. Test Implementation

The first test case we illustrate in this sub-section is to validate the response returned from a remote invocation of the hello method on the service endpoint:

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

It is clear that the remote endpoint method returns the same response as the local method, meaning the web service works as expected.

The next test case demonstrates the use of helloStudent method:

@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 this case, the client submits a Student object to the endpoint and receives a message containing the student's name in return. Like the previous test case, the responses from both remote and local invocations are the same.

The last test case that we show over here is more complicated. As defined by the service endpoint implementation class, each time the client invokes the helloStudent method on the endpoint, the submitted Student object will be stored in a cache. This cache can by retrieved by calling the getStudents method on the endpoint. The following test case confirms that content of the students cache represents what the client has sent to the web service:

@Test public void usingGetStudentsMethod_thenCorrect() { Student student1 = new StudentImpl("Adam"); baeldungProxy.helloStudent(student1); Student student2 = new StudentImpl("Eve"); baeldungProxy.helloStudent(student2); Map students = baeldungProxy.getStudents(); assertEquals("Adam", students.get(1).getName()); assertEquals("Eve", students.get(2).getName()); }

7. Conclusion

This tutorial introduced Apache CXF, a powerful framework to work with web services in Java. It focused on the application of the framework as a standard JAX-WS implementation, while still making use of the framework's specific capabilities at run-time.

Pelaksanaan semua contoh dan coretan kod ini terdapat dalam projek GitHub.