1概要


Apache CXF

はJAX-WS完全準拠フレームワークです。

JAX-WS標準で定義されている機能に加えて、Apache CXFは、WSDLとJavaクラス間の変換機能、生のXMLメッセージを操作するためのAPI、JAX-RSのサポート、Spring Frameworkとの統合などを提供

このチュートリアルは、Apache CXFに関する最初のシリーズで、フレームワークの基本的な特徴を紹介しています。自動生成されたWSDLメタデータやCXFのデフォルト設定などの背後でApache CXFを利用しながら、ソースコードではJAX-WS標準APIのみを使用します。


2 Mavenの依存関係

Apache CXFを使用するために必要な主な依存関係は、__org.apache.cxfです。

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-jaxws</artifactId>
    <version>3.1.6</version>
</dependency>

Apache CXFによって提供される実装を参照する

rg.apache.cxf.jaxws.spi.ProviderImpl

このチュートリアルでは、サービスの公開にサーブレットコンテナを使用しません。そのため、必要なJava型定義を提供するためには別の依存関係が必要です。

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http-jetty</artifactId>
    <version>3.1.6</version>
</dependency>

これらの依存関係の最新版については、https://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22org.apache.cxf%22%20AND%20a%3A%22cxf-をご覧ください。 rt-frontend-jaxws%22[

cxf-rt-frontend-jaxws

]およびhttps://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22org.apache.cxf%22%20AND% 20a%3A%22cxf-rt-transport-http-jetty%22 Maven中央リポジトリの[

cxf-rt-transport-http-jetty

]。


3 Webサービスエンドポイント

サービスエンドポイントの設定に使用される実装クラスから始めましょう。

@WebService(endpointInterface = "com.baeldung.cxf.introduction.Baeldung")
public class BaeldungImpl implements Baeldung {
    private Map<Integer, Student> students
      = new LinkedHashMap<Integer, Student>();

    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<Integer, Student> getStudents() {
        return students;
    }
}

ここで気をつけるべき最も重要なことは

@ WebService

アノテーションの中に

endpointInterface

属性が存在することです。この属性は、Webサービスの抽象規約を定義するインタフェースを指します。

エンドポイントインターフェイスで宣言されているすべてのメソッドシグネチャを実装する必要がありますが、インターフェイスを実装する必要はありません。

ここでは、

BaeldungImpl

実装クラスは、次のエンドポイントインタフェースを依然として実装して、インタフェースのすべての宣言されたメソッドが実装されていることを明確にしていますが、これを行うことはオプションです。

@WebService
public interface Baeldung {
    public String hello(String name);

    public String helloStudent(Student student);

    @XmlJavaTypeAdapter(StudentMapAdapter.class)
    public Map<Integer, Student> getStudents();
}

デフォルトでは、Apache CXFはデータバインディングアーキテクチャとしてJAXBを使用します。

ただし、JAXBは

getStudents

メソッドから返される

Map

のバインディングを直接サポートしていないため、


Map

をJAXBが使用できるJavaクラスに変換するためのアダプタが必要です

さらに、コントラクト要素をその実装から分離するために、

Student

をインタフェースとして定義し、JAXBもインタフェースを直接サポートしていないため、これに対処するためのアダプタがもう1つ必要です。実際、便宜上、

Student

をクラスとして宣言することがあります。インタフェースとしてのこの型の使用は、適応クラスを使用するもう1つのデモンストレーションです。

アダプターは、以下のセクションに示されています。


4カスタムアダプター

この節では、JAXBを使用してJavaインタフェースと

Map

のバインディングをサポートするためにアダプテーションクラスを使用する方法について説明します。


4.1. インターフェースアダプター

これが

Student

インターフェースの定義方法です。

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

このインタフェースは、

String

を返すメソッドを1つだけ宣言し、JAXBバインディングを適用できる型との間でマッピングを行うためのアダプテーションクラスとして

StudentAdapter

を指定します。


StudentAdapter

クラスは次のように定義されています。

public class StudentAdapter extends XmlAdapter<StudentImpl, Student> {
    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

インタフェースを実装し、

marshal

メソッドと

unmarshal

メソッドの実装を提供する必要があります。

marshal

メソッドは、バインドされた型(

Student

、JAXBが直接処理できないインタフェース)を値型(

StudentImpl

、JAXBが処理できる具体的なクラス)に変換します。

unmarshal

メソッドは、逆のことを行います。

これは

StudentImpl

クラスの定義です。

@XmlType(name = "Student")
public class StudentImpl implements Student {
    private String name;

   //constructors, getter and setter
}


4.2.

マップ

アダプタ


Baeldung

エンドポイントインタフェースの

getStudents

メソッドは、

Map

を返し、

Map

をJAXBで処理できる型に変換するための適応クラスを示します。

StudentAdapter

クラスと同様に、このアダプテーションクラスは

XmlAdapter

インターフェースの

marshal

メソッドと

unmarshal

メソッドを実装する必要があります。

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


StudentMapAdapter

クラスは、

Map <Integer、Student>



StudentMap

値型の間のマッピングを次のように定義します。

@XmlType(name = "StudentMap")
public class StudentMap {
    private List<StudentEntry> entries = new ArrayList<StudentEntry>();

    @XmlElement(nillable = false, name = "entry")
    public List<StudentEntry> getEntries() {
        return entries;
    }

    @XmlType(name = "StudentEntry")
    public static class StudentEntry {
        private Integer id;
        private Student student;

       //getters and setters
    }
}

5.展開


5.1.

サーバー

定義

上記のWebサービスをデプロイするために、標準のJAX-WS APIを利用します。私たちはApache CXFを使っているので、フレームワークは追加の仕事をします。 WSDLスキーマの生成と公開

サービスサーバーの定義は次のとおりです。

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

テストを容易にするためにサーバーをしばらくアクティブにした後、システムリソースを解放するためにサーバーをシャットダウンする必要があります。

Thread.sleep

メソッドに

long

引数を渡すことで、必要に応じてサーバーの作業期間を指定できます。


5.2.

Server


のデプロイ

このチュートリアルでは、

org.codehaus.mojo:-maven-plugin

プラグインを使用して上記のサーバーをインスタンス化し、そのライフサイクルを制御します。これはMaven POMファイルで次のように宣言されています。

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <configuration>
        <mainClass>com.baeldung.cxf.introduction.Server</mainClass>
    </configuration>
</plugin>


mainClass

設定は、Webサービスエンドポイントが公開されている

Server

クラスを参照します。このプラグインの

java

目標を達成した後、私たちは


6. テストケース

このセクションでは、以前に作成したWebサービスを検証するために使用されるテストケースを作成する手順について説明します。

テストを実行する前に、Webサービスサーバーを起動するために

exec:java

ゴールを実行する必要があることに注意してください。


6.1. 準備

最初のステップは、テストクラスにいくつかのフィールドを宣言することです。

public class StudentTest {
    private static QName SERVICE__NAME
      = new QName("http://introduction.cxf.baeldung.com/", "Baeldung");
    private static QName PORT__NAME
      = new QName("http://introduction.cxf.baeldung.com/", "BaeldungPort");

    private Service service;
    private Baeldung baeldungProxy;
    private BaeldungImpl baeldungImpl;

   //other declarations
}

次のinitializerブロックは、テストを実行する前に

javax.xml.ws.Service

タイプの

service

フィールドを初期化するために使用されます。

{
    service = Service.create(SERVICE__NAME);
    String endpointAddress = "http://localhost:8080/baeldung";
    service.addPort(PORT__NAME, SOAPBinding.SOAP11HTTP__BINDING, endpointAddress);
}

POMファイルにJUnit依存関係を追加した後、以下のコードスニペットのように

@ Before

アノテーションを使用できます。このメソッドは

Baeldung

フィールドを再インスタンス化するためにあらゆるテストの前に実行されます。

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


baeldungProxy

変数はWebサービスエンドポイントのプロキシですが、

baeldungImpl

は単なるJavaオブジェクトです。このオブジェクトは、プロキシを介したリモートエンドポイントメソッドの呼び出しの結果とローカルメソッドの呼び出しの結果を比較するために使用されます。


QName

インスタンスは、名前空間URIとローカル部分の2つの部分で識別されます。

Service.getPort

メソッドの

QName

タイプの

PORT

NAME

引数が省略された場合、Apache CXFは引数の名前空間URIが逆の順序でエンドポイントインタフェースのパッケージ名であると仮定し、そのローカル部分は

Port

によって追加されたインタフェース名ですこれは、

PORT__NAMEとまったく同じ値です。したがって、このチュートリアルでは、この引数を省略することができます。


6.2. テスト実施

このサブセクションで説明する最初のテストケースは、サービスエンドポイントの

hello

メソッドのリモート呼び出しから返された応答を検証することです。

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

リモートエンドポイントメソッドがローカルメソッドと同じ応答を返すことは明らかです。つまり、Webサービスは期待どおりに機能します。

次のテストケースは

helloStudent

メソッドの使い方を示しています。

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

この場合、クライアントは

Student

オブジェクトをエンドポイントに送信し、代わりに生徒の名前を含むメッセージを受け取ります。前のテストケースと同様に、リモート呼び出しとローカル呼び出しの両方からの応答は同じです。

ここで紹介する最後のテストケースはもっと複雑です。サービスエンドポイント実装クラスで定義されているように、クライアントがエンドポイントで

helloStudent

メソッドを呼び出すたびに、送信された

Student

オブジェクトはキャッシュに格納されます。このキャッシュは、エンドポイントで

getStudents

メソッドを呼び出すことによって取得できます。次のテストケースでは、

students

キャッシュの内容がクライアントからWebサービスに送信された内容を表していることを確認しています。

@Test
public void usingGetStudentsMethod__thenCorrect() {
    Student student1 = new StudentImpl("Adam");
    baeldungProxy.helloStudent(student1);

    Student student2 = new StudentImpl("Eve");
    baeldungProxy.helloStudent(student2);

    Map<Integer, Student> students = baeldungProxy.getStudents();
    assertEquals("Adam", students.get(1).getName());
    assertEquals("Eve", students.get(2).getName());
}


7. 結論

このチュートリアルでは、Apache CXFを紹介しました。これは、JavaでWebサービスを扱うための強力なフレームワークです。実行時にはまだフレームワーク固有の機能を利用しながら、標準のJAX-WS実装としてのフレームワークの適用に焦点を当てていました。

これらすべての例とコードスニペットの実装はhttps://github.com/eugenp/tutorials/tree/master/apache-cxf/cxf-introduction[a GitHub project]にあります。