Pengantar Apache CXF

1. Ikhtisar

Apache CXF adalah kerangka kerja yang sepenuhnya kompatibel dengan JAX-WS.

Selain fitur yang ditentukan oleh standar JAX-WS, Apache CXF menyediakan kemampuan konversi antara kelas WSDL dan Java, API yang digunakan untuk memanipulasi pesan XML mentah, dukungan untuk JAX-RS, integrasi dengan Spring Framework, dll.

Tutorial ini adalah yang pertama dari seri Apache CXF, yang memperkenalkan karakteristik dasar framework. Ini hanya menggunakan API standar JAX-WS dalam kode sumber sementara masih memanfaatkan Apache CXF di belakang layar, seperti metadata WSDL yang dibuat secara otomatis dan konfigurasi default CXF.

2. Ketergantungan Maven

Ketergantungan kunci yang diperlukan untuk menggunakan Apache CXF adalah org.apache.cxf: cxf - rt - frontend - jaxws . Ini menyediakan implementasi JAX-WS untuk menggantikan JDK built-in:

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

Perhatikan bahwa artefak ini berisi file bernama javax.xml.ws.spi.Provider di dalam direktori META-INF / services . Java VM melihat baris pertama file ini untuk menentukan implementasi JAX-WS yang akan digunakan. Dalam kasus ini, konten baris adalah o rg.apache.cxf.jaxws.spi.ProviderImpl , mengacu pada implementasi yang disediakan oleh Apache CXF.

Dalam tutorial ini, kami tidak menggunakan wadah servlet untuk menerbitkan layanan, oleh karena itu ketergantungan lain diperlukan untuk memberikan definisi tipe Java yang diperlukan:

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

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

3. Titik Akhir Layanan Web

Mari kita mulai dengan kelas implementasi yang digunakan untuk mengonfigurasi titik akhir layanan:

@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; } }

Hal terpenting yang harus diperhatikan di sini adalah keberadaan atribut endpointInterface di anotasi @WebService . Atribut ini menunjuk ke antarmuka yang menentukan kontrak abstrak untuk layanan web.

Semua tanda tangan metode yang dideklarasikan di antarmuka titik akhir perlu diimplementasikan, tetapi tidak diperlukan untuk mengimplementasikan antarmuka.

Di sini kelas implementasi BaeldungImpl masih mengimplementasikan antarmuka titik akhir berikut untuk memperjelas bahwa semua metode antarmuka yang dideklarasikan telah diimplementasikan, tetapi melakukan ini opsional:

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

Secara default, Apache CXF menggunakan JAXB sebagai arsitektur pengikatan datanya. Namun, karena JAXB tidak secara langsung mendukung pengikatan Map , yang dikembalikan dari metode getStudents , kita memerlukan adaptor untuk mengonversi Map ke kelas Java yang dapat digunakan JAXB .

Selain itu, untuk memisahkan elemen kontrak dari implementasinya, kami mendefinisikan Student sebagai antarmuka dan JAXB juga tidak mendukung antarmuka secara langsung, sehingga kami membutuhkan satu adaptor lagi untuk menangani hal ini. Bahkan, untuk kenyamanan, kami dapat mendeklarasikan Siswa sebagai kelas. Penggunaan tipe ini sebagai antarmuka hanyalah satu lagi demonstrasi penggunaan kelas adaptasi.

Adaptor didemonstrasikan di bagian kanan bawah.

4. Adaptor Kustom

Bagian ini mengilustrasikan cara menggunakan kelas adaptasi untuk mendukung pengikatan antarmuka Java dan Peta menggunakan JAXB.

4.1. Adaptor Antarmuka

Ini adalah bagaimana antarmuka Mahasiswa didefinisikan:

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

Antarmuka ini mendeklarasikan hanya satu metode yang mengembalikan String dan menetapkan StudentAdapter sebagai kelas adaptasi untuk memetakan dirinya sendiri ke dan dari jenis yang dapat menerapkan pengikatan JAXB.

Kelas StudentAdapter didefinisikan sebagai 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 adaptasi harus mengimplementasikan antarmuka XmlAdapter dan menyediakan implementasi untuk metode marshal dan unmarshal . The marshal metode mengubah jenis terikat ( Mahasiswa , antarmuka yang JAXB tidak bisa langsung menangani) menjadi jenis nilai ( StudentImpl , kelas konkret yang dapat diproses oleh JAXB). The unmarshal Metode melakukan hal-hal di sekitar cara lain.

Berikut adalah definisi kelas StudentImpl :

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

4.2. Adaptor Peta

The getStudents metode Baeldung antarmuka endpoint mengembalikan Peta dan menunjukkan kelas adaptasi untuk mengkonversi Peta untuk jenis yang dapat ditangani oleh JAXB. Mirip dengan kelas StudentAdapter , kelas adaptasi ini harus mengimplementasikan metode marshal dan unmarshal dari antarmuka 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; } }
    

Kelas StudentMapAdapter memetakan Peta ke dan dari tipe nilai StudentMap dengan definisi sebagai 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. Penerapan

5.1. Definisi Server

Untuk menerapkan layanan web yang dibahas di atas, kami akan menggunakan API JAX-WS standar. Karena kami menggunakan Apache CXF, kerangka kerja melakukan beberapa pekerjaan tambahan, misalnya membuat dan menerbitkan skema WSDL. Berikut adalah bagaimana server layanan didefinisikan:

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 server aktif beberapa saat untuk memfasilitasi pengujian, server harus dimatikan untuk melepaskan sumber daya sistem. Anda dapat menentukan durasi kerja apa pun untuk server berdasarkan kebutuhan Anda dengan meneruskan argumen panjang ke metode Thread.sleep .

5.2. Penyebaran Server

Dalam tutorial ini, kami menggunakan plugin org.codehaus.mojo: exec -maven- plugin untuk membuat instance server yang diilustrasikan di atas dan mengontrol siklus hidupnya. Ini dideklarasikan dalam file POM Maven sebagai berikut:

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

The MainClass konfigurasi mengacu pada Server kelas di mana titik akhir layanan web diterbitkan. Setelah menjalankan tujuan java dari plugin ini, kita dapat memeriksa skema WSDL yang dibuat secara otomatis oleh Apache CXF dengan mengakses URL // localhost: 8080 / baeldung? Wsdl .

6. Kasus Uji

Bagian ini memandu Anda melalui langkah-langkah untuk menulis kasus uji yang digunakan untuk memverifikasi layanan web yang kami buat sebelumnya.

Harap dicatat bahwa kami perlu menjalankan tujuan exec: java untuk memulai server layanan web sebelum menjalankan pengujian apa pun.

6.1. Persiapan

Langkah pertama adalah mendeklarasikan beberapa kolom untuk kelas pengujian:

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 }

Blok penginisialisasi berikut digunakan untuk memulai bidang layanan jenis javax.xml.ws.Service sebelum menjalankan tes apa pun:

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

Setelah menambahkan dependensi JUnit ke file POM, kita dapat menggunakan anotasi @Before seperti pada potongan kode di bawah ini. Metode ini berjalan sebelum setiap pengujian untuk membuat ulang bidang Baeldung :

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

The baeldungProxy variabel proxy untuk endpoint layanan web, sedangkan baeldungImpl hanya objek Java sederhana. Objek ini digunakan untuk membandingkan hasil pemanggilan metode titik akhir jarak jauh melalui proxy dengan pemanggilan metode lokal.

Perhatikan bahwa instance QName diidentifikasi oleh dua bagian: URI Namespace dan bagian lokal. Jika argumen PORT_NAME , dari jenis QName , metode Service.getPort dihilangkan, Apache CXF akan menganggap bahwa Namespace URI argumen adalah nama paket antarmuka titik akhir dalam urutan terbalik dan bagian lokalnya adalah nama antarmuka yang ditambahkan oleh Port , yang merupakan nilai yang sama persis dari PORT_NAME. Oleh karena itu, dalam tutorial ini kami mungkin mengabaikan argumen ini.

6.2. Implementasi Tes

Kasus uji pertama yang kami ilustrasikan di sub-bagian ini adalah untuk memvalidasi respons yang dikembalikan dari pemanggilan jarak jauh metode hello pada titik akhir layanan:

@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.

Penerapan semua contoh dan cuplikan kode ini dapat ditemukan di proyek GitHub.