Въведение в Apache CXF

1. Общ преглед

Apache CXF е JAX-WS напълно съвместима рамка.

В допълнение към функциите, дефинирани от стандартите JAX-WS, Apache CXF предоставя възможност за преобразуване между WSDL и Java класове, API, използвани за манипулиране на необработени XML съобщения, поддръжка за JAX-RS, интеграция с Spring Framework и др.

Този урок е първият от поредицата за Apache CXF, представящ основните характеристики на рамката. Той използва само стандартните API на JAX-WS в изходния код, като същевременно се възползва от Apache CXF зад кулисите, като автоматично генерирани метаданни WSDL и конфигурация по подразбиране на CXF.

2. Зависимости на Maven

Ключовата зависимост, необходима за използване на Apache CXF, е org.apache.cxf: cxf - rt - frontend - jaxws . Това осигурява внедряване на JAX-WS, за да замени вграденото JDK:

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

Забележете, че този артефакт съдържа файл с име javax.xml.ws.spi.Provider вътре в директорията META-INF / services . Java VM разглежда първия ред на този файл, за да определи внедряването на JAX-WS, което да използва. В този случай съдържанието на реда е o rg.apache.cxf.jaxws.spi.ProviderImpl , позовавайки се на изпълнението, предоставено от Apache CXF.

В този урок не използваме контейнер за сървлети за публикуване на услугата, поради което се изисква друга зависимост, за да предоставим необходимите дефиниции на Java тип:

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

За най-новите версии на тези зависимости, моля, проверете cxf-rt-frontend-jaxws и cxf-rt-transports-http-jetty в централното хранилище на Maven.

3. Крайна точка на уеб услугата

Нека започнем с класа на внедряване, използван за конфигуриране на крайната точка на услугата:

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

Най-важното нещо, което трябва да се забележи тук, е наличието на атрибута endpointInterface в анотацията @WebService . Този атрибут сочи към интерфейс, дефиниращ абстрактен договор за уеб услугата.

Всички подписи на методи, декларирани в интерфейса на крайната точка, трябва да бъдат внедрени, но не е необходимо да се прилага интерфейсът.

Тук класът на изпълнение на BaeldungImpl все още изпълнява следния интерфейс на крайната точка, за да стане ясно, че всички декларирани методи на интерфейса са внедрени, но това не е задължително:

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

По подразбиране Apache CXF използва JAXB като своя архитектура за свързване на данни. Тъй като обаче JAXB не поддържа директно свързване на карта , която се връща от метода getStudents , се нуждаем от адаптер за конвертиране на картата в клас Java, който JAXB може да използва .

Освен това, за да отделим елементите на договора от тяхното изпълнение, ние определяме Student като интерфейс и JAXB също не поддържа директно интерфейси, поради което се нуждаем от още един адаптер, за да се справим с това. Всъщност за удобство можем да декларираме Student като клас. Използването на този тип като интерфейс е само още една демонстрация на използването на класове за адаптация.

Адаптерите са демонстрирани в раздела точно по-долу.

4. Персонализирани адаптери

Този раздел илюстрира начина за използване на класове за адаптация за поддържане на свързване на Java интерфейс и карта с помощта на JAXB.

4.1. Интерфейсен адаптер

Ето как се определя интерфейсът на Student :

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

Този интерфейс декларира само един метод, връщащ String, и посочва StudentAdapter като клас на адаптация, за да се прикачи към и от тип, който може да приложи JAXB обвързване.

Класът StudentAdapter се дефинира както следва:

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

Класът на адаптация трябва да реализира интерфейса XmlAdapter и да осигури изпълнение за методите маршал и немаршал . Най- маршал метод превръща подвързан тип ( Student , интерфейс, който JAXB, не могат пряко дръжка) в стойностен тип ( StudentImpl , клас на бетона, които могат да бъдат обработвани от JAXB). В unmarshal метод прави нещата по друг начин.

Ето дефиницията на класа StudentImpl :

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

4.2. Адаптер за карта

Методът getStudents на интерфейса на крайната точка Baeldung връща Map и посочва клас на адаптация за преобразуване на Map в тип, който може да се обработва от JAXB. Подобно на класа StudentAdapter , този клас на адаптация трябва да реализира маршалски и немаршалски методи на интерфейса 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; } }
    

Класът StudentMapAdapter картографира Map към и от типа стойност StudentMap с дефиницията, както следва:

@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. Разполагане

5.1. Определение на сървъра

За да разгърнем обсъдената по-горе уеб услуга, ще използваме стандартните API на JAX-WS. Тъй като използваме Apache CXF, рамката върши допълнителна работа, например генериране и публикуване на WSDL схемата. Ето как се определя сървърът на услугата:

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

След като сървърът е активен за известно време, за да улесни тестването, той трябва да бъде изключен, за да освободи системните ресурси. Можете да посочите всяка продължителност на работа за сървъра въз основа на вашите нужди, като предадете дълъг аргумент на метода Thread.sleep .

5.2. Внедряване на сървъра

В този урок използваме приставката org.codehaus.mojo: exec -maven- plugin, за да създадем екземпляр на илюстрирания по-горе сървър и да контролираме неговия жизнен цикъл. Това е декларирано във файла Maven POM, както следва:

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

В mainClass конфигурация се отнася до сървър клас, където се публикува крайната точка на уеб услуга. След като стартираме java целта на този плъгин, можем да проверим автоматично генерираната от Apache CXF схема WSDL чрез достъп до URL // localhost: 8080 / baeldung? Wsdl .

6. Тестови случаи

Този раздел ще ви преведе през стъпки за писане на тестови случаи, използвани за проверка на уеб услугата, която създадохме преди.

Моля, обърнете внимание, че трябва да изпълним целта exec: java, за да стартираме сървъра на уеб услугата, преди да стартираме какъвто и да е тест.

6.1. Подготовка

Първата стъпка е да декларирате няколко полета за тестовия клас:

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.

Прилагането на всички тези примери и кодови фрагменти може да бъде намерено в проект на GitHub.