Apache CXF Aegisデータバインディングの紹介

Apache CXF Aegisデータバインディングの概要

1. 概要

このチュートリアルでは、Aegisデータバインディングの概要を説明します。これは、JavaオブジェクトとXMLスキーマで記述されたXMLドキュメントの間でマッピングできるサブシステムです。 Aegisを使用すると、プログラミングの労力を最小限に抑えながら、マッピングプロセスを詳細に制御できます。

AegisはApache CXFの一部ですが、このフレームワーク内でのみ使用するように制約されていません。 代わりに、このデータバインディングメカニズムはどこでも使用できるため、このチュートリアルでは、独立したサブシステムとしての使用に焦点を当てます。

2. Mavenの依存関係

Aegisデータバインディングをアクティブにするために必要な唯一の依存関係は次のとおりです。


    org.apache.cxf
    cxf-rt-databinding-aegis
    3.1.8

このアーティファクトの最新バージョンはhereにあります。

3. タイプ定義

このセクションでは、イージスを説明するために使用される3つのタイプの定義について説明します。

3.1. Course

これは、この例で最も単純なクラスであり、次のように定義されています。

public class Course {
    private int id;
    private String name;
    private String instructor;
    private Date enrolmentDate;

    // standard getters and setters
}

3.2. CourseRepo

CourseRepoは、モデルの最上位タイプです。 クラスではなくインターフェースとして定義し、Javaインターフェースをマーシャリングするのがいかに簡単かを示します。これは、カスタムアダプターなしではJAXBでは不可能です。

public interface CourseRepo {
    String getGreeting();
    void setGreeting(String greeting);
    Map getCourses();
    void setCourses(Map courses);
    void addCourse(Course course);
}

戻り値の型MapgetCoursesメソッドを宣言していることに注意してください。 これは、JAXBに対するAegisのもう1つの利点を示すためのものです。 前者は可能ですが、後者はカスタムアダプタなしでマップをマーシャリングできません。

3.3. CourseRepoImpl

このクラスは、CourseRepoインターフェースへの実装を提供します。

public class CourseRepoImpl implements CourseRepo {
    private String greeting;
    private Map courses = new HashMap<>();

    // standard getters and setters

    @Override
    public void addCourse(Course course) {
        courses.put(course.getId(), course);
    }
}

4. カスタムデータバインディング

カスタマイズを有効にするには、XMLマッピングファイルがクラスパスに存在する必要があります。 これらのファイルは、関連するJavaタイプのパッケージ階層に対応する構造を持つディレクトリに配置する必要があります。

たとえば、完全修飾名クラスの名前がpackage.ClassNameの場合、関連するマッピングファイルはクラスパスのpackage/ClassNameサブディレクトリ内にある必要があります。 マッピングファイルの名前は、.aegis.xmlサフィックスが追加された関連するJavaタイプと同じである必要があります。

4.1. CourseRepoマッピング

CourseRepoインターフェイスはcom.example.cxf.aegisパッケージに属しているため、対応するマッピングファイルはCourseRepo.aegis.xmlという名前で、クラスパスのcom/example/cxf/aegisディレクトリに配置されます。

CourseRepoマッピングファイルで、CourseRepoインターフェイスに関連付けられているXML要素の名前と名前空間、およびそのgreetingプロパティのスタイルを変更します。


    
        
    

4.2. コースマッピング

CourseRepoタイプと同様に、クラスCourseのマッピングファイルはCourse.aegis.xmlという名前で、com/example/cxf/aegisディレクトリにもあります。

このマッピングファイルでは、マーシャリング時にCourseクラスのinstructorプロパティを無視するように、Aegisに指示します。これにより、出力XMLドキュメントから再作成されたオブジェクトでその値を使用できなくなります。


    
        
    

Aegis' home pageは、より多くのカスタマイズオプションを見つけることができる場所です。

5. テスト

このセクションは、Aegisデータバインディングの使用法を説明するテストケースをセットアップして実行するためのステップバイステップガイドです。

テストプロセスを容易にするために、テストクラス内で2つのフィールドを宣言します。

public class exampleTest {
    private AegisContext context;
    private String fileName = "example.xml";

    // other methods
}

これらのフィールドは、このクラスの他のメソッドで使用されるようにここで定義されています。

5.1. AegisContext初期化

まず、AegisContextオブジェクトを作成する必要があります。

context = new AegisContext();

次に、そのAegisContextインスタンスが構成され、初期化されます。 コンテキストのルートクラスを設定する方法は次のとおりです。

Set rootClasses = new HashSet();
rootClasses.add(CourseRepo.class);
context.setRootClasses(rootClasses);

Aegisは、Set<Type>オブジェクト内のTypeごとにXMLマッピング要素を作成します。 このチュートリアルでは、ルートタイプとしてCourseRepoのみを設定します。

次に、コンテキストの実装マップを設定して、CourseRepoインターフェイスのプロキシクラスを指定します。

Map, String> beanImplementationMap = new HashMap<>();
beanImplementationMap.put(CourseRepoImpl.class, "CourseRepo");
context.setBeanImplementationMap(beanImplementationMap);

Aegisコンテキストの最後の構成は、対応するXMLドキュメントにxsi:type属性を設定するように指示しています。 この属性は、マッピングファイルによってオーバーライドされない限り、関連付けられたJavaオブジェクトの実際のタイプ名を保持します。

context.setWriteXsiTypes(true);

これで、AegisContextインスタンスを初期化する準備が整いました。

context.initialize();

コードをクリーンに保つために、このサブセクションのすべてのコードスニペットを1つのヘルパーメソッドに収集します。

private void initializeContext() {
    // ...
}

5.2. 簡単なデータ設定

このチュートリアルは単純な性質のため、永続的なソリューションに依存するのではなく、メモリ内でサンプルデータを直接生成します。 以下のセットアップロジックを使用して、コースリポジトリにデータを入力しましょう。

private CourseRepoImpl initCourseRepo() {
    Course restCourse = new Course();
    restCourse.setId(1);
    restCourse.setName("REST with Spring");
    restCourse.setInstructor("Eugen");
    restCourse.setEnrolmentDate(new Date(1234567890000L));

    Course securityCourse = new Course();
    securityCourse.setId(2);
    securityCourse.setName("Learn Spring Security");
    securityCourse.setInstructor("Eugen");
    securityCourse.setEnrolmentDate(new Date(1456789000000L));

    CourseRepoImpl courseRepo = new CourseRepoImpl();
    courseRepo.setGreeting("Welcome to Beldung!");
    courseRepo.addCourse(restCourse);
    courseRepo.addCourse(securityCourse);
    return courseRepo;
}

5.3. JavaオブジェクトとXML要素のバインド

JavaオブジェクトをXML要素にマーシャリングするために必要な手順は、次のヘルパーメソッドを使用して説明されています。

private void marshalCourseRepo(CourseRepo courseRepo) throws Exception {
    AegisWriter writer = context.createXMLStreamWriter();
    AegisType aegisType = context.getTypeMapping().getType(CourseRepo.class);
    XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance()
      .createXMLStreamWriter(new FileOutputStream(fileName));

    writer.write(courseRepo,
      new QName("http://aegis.cxf.example.com", "example"), false, xmlWriter, aegisType);

    xmlWriter.close();
}

ご覧のとおり、AegisWriterおよびAegisTypeオブジェクトはAegisContextインスタンスから作成する必要があります。 次に、AegisWriterオブジェクトは、指定されたJavaインスタンスを指定された出力にマーシャリングします。

この場合、これは、ファイルシステムのfileNameクラスレベルフィールドの値にちなんで名付けられたファイルに関連付けられたXMLStreamWriterオブジェクトです。

次のメソッドは、XMLドキュメントを指定されたタイプのJavaオブジェクトに非整列化します。

private CourseRepo unmarshalCourseRepo() throws Exception {
    AegisReader reader = context.createXMLStreamReader();
    XMLStreamReader xmlReader = XMLInputFactory.newInstance()
      .createXMLStreamReader(new FileInputStream(fileName));

    CourseRepo courseRepo = (CourseRepo) reader.read(
      xmlReader, context.getTypeMapping().getType(CourseRepo.class));

    xmlReader.close();
    return courseRepo;
}

ここで、AegisReaderオブジェクトはAegisContextインスタンスから生成されます。 次に、AegisReaderオブジェクトは、提供された入力からJavaオブジェクトを作成します。 この例では、その入力は、上記のmarshalCourseRepoメソッドで生成したファイルに基づくXMLStreamReaderオブジェクトです。

5.4. アサーション

次は、前のサブセクションで定義したすべてのヘルパーメソッドをテストメソッドに結合します。

@Test
public void whenMarshalingAndUnmarshalingCourseRepo_thenCorrect()
  throws Exception {
    initializeContext();
    CourseRepo inputRepo = initCourseRepo();
    marshalCourseRepo(inputRepo);
    CourseRepo outputRepo = unmarshalCourseRepo();
    Course restCourse = outputRepo.getCourses().get(1);
    Course securityCourse = outputRepo.getCourses().get(2);

    // JUnit assertions
}

最初にCourseRepoインスタンスを作成し、次にそれをXMLドキュメントにマーシャリングし、最後にドキュメントをアンマーシャリングして元のオブジェクトを再作成します。 再作成されたオブジェクトが期待どおりであることを確認しましょう。

assertEquals("Welcome to Beldung!", outputRepo.getGreeting());
assertEquals("REST with Spring", restCourse.getName());
assertEquals(new Date(1234567890000L), restCourse.getEnrolmentDate());
assertNull(restCourse.getInstructor());
assertEquals("Learn Spring Security", securityCourse.getName());
assertEquals(new Date(1456789000000L), securityCourse.getEnrolmentDate());
assertNull(securityCourse.getInstructor());

instructorプロパティを除いて、タイプDateの値を持つenrolmentDateプロパティを含め、他のすべてのプロパティの値が回復されることは明らかです。 これは、Courseオブジェクトをマーシャリングするときにinstructorプロパティを無視するようにAegisに指示したため、まさに私たちが期待していることです。

5.5. XMLドキュメントを出力する

Aegisマッピングファイルの効果を明確にするために、カスタマイズなしのXMLドキュメントを以下に示します。


    
        
            1
            
                2009-02-14T06:31:30+07:00
                
                1
                Eugen
                REST with Spring
            
        
        
            2
            
                2016-03-01T06:36:40+07:00
                
                2
                Eugen
                Learn Spring Security
            
        
    
    Welcome to Beldung!

これをAegisカスタムマッピングが動作している場合と比較してください。


    
        
            1
            
                2009-02-14T06:31:30+07:00
                
                1
                REST with Spring
            
        
        
            2
            
                2016-03-01T06:36:40+07:00
                
                2
                Learn Spring Security
            
        
    

このXML構造は、このセクションで定義されたテストを実行した後、プロジェクトのメインディレクトリ内のexample.xmlにあります。

CourseRepoオブジェクトに対応するXML要素のtype属性と名前空間が、CourseRepo.aegis.xmlファイルで設定した内容に従って変化することがわかります。 greetingプロパティも属性に変換され、Courseオブジェクトのinstructorプロパティは期待どおりに消えます。

デフォルトでは、Aegisは基本的なJava型を最適なスキーマ型に変換することに注意してください。 このチュートリアルに示すように、Dateオブジェクトからxsd:dateTime要素へ。 ただし、対応するマッピングファイルで構成を設定することにより、特定のバインディングを変更できます。

詳細情報が必要な場合は、Aegis home pageに移動してください。

6. 結論

このチュートリアルでは、Apache CXF Aegisデータバインディングをスタンドアロンサブシステムとして使用する方法を説明します。 Aegisを使用してJavaオブジェクトをXML要素にマップする方法、およびその逆の方法を示しています。

このチュートリアルでは、データバインディングの動作をカスタマイズする方法にも焦点を当てています。

そして、いつものように、これらすべての例とコードスニペットの実装はthe GitHub projectにあります。