Introduction à la liaison de données Aegis Apache CXF

Introduction à la liaison de données Apache CXF Aegis

1. Vue d'ensemble

Ce tutoriel donne une introduction à la liaison de donnéesAegis, un sous-système qui peut mapper entre des objets Java et des documents XML décrits par des schémas XML. Aegis permet un contrôle détaillé du processus de mappage tout en minimisant les efforts de programmation.

Aegis fait partie deApache CXF, mais n'est pas obligé d'être utilisé dans ce cadre uniquement. Au lieu de cela, ce mécanisme de liaison de données peut être utilisé n'importe où et, dans ce tutoriel, nous nous concentrons sur son utilisation en tant que sous-système indépendant.

2. Dépendances Maven

La seule dépendance requise pour activer la liaison de données Aegis est la suivante:


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

La dernière version de cet artefact peut être trouvéehere.

3. Définitions de type

Cette section passe en revue les définitions de trois types utilisés pour illustrer Aegis.

3.1. Course

C'est la classe la plus simple de notre exemple, définie comme suit:

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

    // standard getters and setters
}

3.2. CourseRepo

CourseRepo est le type de niveau supérieur dans notre modèle. Nous le définissons comme une interface plutôt que comme une classe pour montrer à quel point il est facile de marshaler une interface Java, ce qui est impossible dans JAXB sans un adaptateur personnalisé:

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

Notez que nous déclarons la méthodegetCourses avec le type de retourMap. Ceci est intentionnel pour exprimer un autre avantage de Aegis par rapport à JAXB. Ce dernier ne peut pas marshal une carte sans un adaptateur personnalisé alors que le premier peut.

3.3. CourseRepoImpl

Cette classe fournit une implémentation à l'interfaceCourseRepo:

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. Liaison de données personnalisée

Pour que la personnalisation prenne effet, les fichiers de mappage XML doivent être présents sur le chemin d'accès aux classes. Il est nécessaire que ces fichiers soient placés dans un répertoire dont la structure correspond à la hiérarchie des packages des types Java associés.

Par exemple, si une classe de noms complets est nomméepackage.ClassName, son fichier de mappage associé doit se trouver dans le sous-répertoirepackage/ClassName sur le chemin de classe. Le nom d'un fichier de mappage doit être égal au type Java associé avec un suffixe.aegis.xml ajouté.

4.1. MappageCourseRepo

L'interfaceCourseRepo appartient au packagecom.example.cxf.aegis, donc son fichier de mappage correspondant est nomméCourseRepo.aegis.xml et placé dans le répertoirecom/example/cxf/aegis sur le chemin des classes.

Dans le fichier de mappage deCourseRepo, nous changeons le nom et l'espace de noms de l'élément XML associé à l'interfaceCourseRepo, ainsi que le style de sa propriétégreeting:


    
        
    

4.2. Cartographie des cours

Similaire au typeCourseRepo, le fichier de mappage de la classeCourse est nomméCourse.aegis.xml et se trouve également dans le répertoirecom/example/cxf/aegis.

Dans ce fichier de mappage, nous demandons à Aegis d'ignorer la propriétéinstructor de la classeCourse lors du marshaling, afin que sa valeur ne soit pas disponible dans l'objet recréé à partir du document XML de sortie:


    
        
    

Aegis' home page est l'endroit où nous pouvons trouver plus d'options de personnalisation.

5. Essai

Cette section est un guide pas à pas pour configurer et exécuter un scénario de test illustrant l'utilisation des liaisons de données Aegis.

Pour faciliter le processus de test, nous déclarons deux champs dans la classe de test:

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

    // other methods
}

Ces champs ont été définis ici pour être utilisés par d'autres méthodes de cette classe.

5.1. Initialisation deAegisContext

Tout d'abord, un objetAegisContext doit être créé:

context = new AegisContext();

Cette instanceAegisContext est ensuite configurée et initialisée. Voici comment nous avons défini les classes racines pour le contexte:

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

Aegis crée un élément de mappage XML pour chaqueType dans l'objetSet<Type>. Dans ce didacticiel, nous définissons uniquementCourseRepo comme type racine.

Maintenant, définissons le mappage d'implémentation du contexte pour spécifier la classe proxy pour l'interfaceCourseRepo:

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

La dernière configuration du contexte Aegis lui dit de définir l'attributxsi:type dans le document XML correspondant. Cet attribut porte le nom de type réel de l'objet Java associé, sauf substitution par le fichier de mappage:

context.setWriteXsiTypes(true);

Notre instanceAegisContext est maintenant prête à être initialisée:

context.initialize();

Pour garder le code propre, nous recueillons tous les extraits de code de cette sous-section en une seule méthode d'assistance:

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

5.2. Configuration simple des données

En raison de la nature simple de ce didacticiel, nous générons des exemples de données directement en mémoire plutôt que de nous appuyer sur une solution persistante. Remplissons le référentiel de cours à l'aide de la logique de configuration ci-dessous:

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. Liaison d'objets Java et d'éléments XML

Les étapes à suivre pour structurer les objets Java en éléments XML sont illustrées à l'aide de la méthode d'assistance suivante:

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

Comme nous pouvons le voir, les objetsAegisWriter etAegisType doivent être créés à partir de l'instanceAegisContext. L'objetAegisWriter rassemble ensuite l'instance Java donnée vers la sortie spécifiée.

Dans ce cas, il s'agit d'un objetXMLStreamWriter associé à un fichier nommé d'après la valeur du champ de niveau classefileName dans le système de fichiers.

La méthode suivante permet de disséminer un document XML en un objet Java du type donné:

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

Ici, un objetAegisReader est généré à partir de l'instanceAegisContext. L'objetAegisReader crée ensuite un objet Java à partir de l'entrée fournie. Dans cet exemple, cette entrée est un objetXMLStreamReader sauvegardé par le fichier que nous avons généré dans la méthodemarshalCourseRepo décrite ci-dessus.

5.4. Assertions

Maintenant, il est temps de combiner toutes les méthodes d'assistance définies dans les sous-sections précédentes en une méthode de test:

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

Nous créons d'abord une instance deCourseRepo, puis nous la rassemblons dans un document XML et enfin démarshalons le document pour recréer l'objet d'origine. Vérifions que l'objet recréé correspond à ce que nous attendons:

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

Il est clair qu'à l'exception de la propriétéinstructor, toutes les autres ont leurs valeurs récupérées, y compris la propriétéenrolmentDate avec des valeurs de typeDate. C'est exactement ce à quoi nous nous attendons car nous avons demandé à Aegis d'ignorer la propriétéinstructor lors du marshaling des objetsCourse.

5.5. Document XML de sortie

Pour rendre explicite l'effet des fichiers de mappage Aegis, nous montrons le document XML sans personnalisation ci-dessous:


    
        
            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!

Comparez cela avec le cas où le mappage personnalisé Aegis est en action:


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

Vous pouvez trouver cette structure XML dans lesexample.xml, juste dans le répertoire principal du projet après avoir exécuté le test défini dans cette section.

Vous verrez que l'attributtype et l'espace de noms de l'élément XML correspondant à l'objetCourseRepo changent conformément à ce que nous avons défini dans le fichierCourseRepo.aegis.xml. La propriétégreeting est également transformée en attribut et la propriétéinstructor des objetsCourse disparaît comme prévu.

Il est intéressant de noter que par défaut, Aegis convertit un type Java de base en type de schéma correspondant le mieux, par exemple. des objetsDate aux élémentsxsd:dateTime comme indiqué dans ce didacticiel. Toutefois, nous pouvons modifier cette liaison en définissant la configuration dans le fichier de mappage correspondant.

Veuillez naviguer vers lesAegis home page si vous souhaitez avoir plus d'informations.

6. Conclusion

Ce tutoriel illustre l'utilisation de la liaison de données Apache CXF Aegis en tant que sous-système autonome. Il montre comment Aegis peut être utilisé pour mapper des objets Java sur des éléments XML, et inversement.

Le didacticiel explique également comment personnaliser les comportements de liaison de données.

Et, comme toujours, l'implémentation de tous ces exemples et extraits de code peut être trouvée dansthe GitHub project.