Introdução à ligação de dados do Apache CXF Aegis
1. Visão geral
Este tutorial fornece uma introdução à vinculação de dadosAegis, um subsistema que pode mapear entre objetos Java e documentos XML descritos por esquemas XML. O Aegis permite controle detalhado sobre o processo de mapeamento, mantendo ao mínimo o esforço de programação.
Aegis é parte deApache CXF, mas não está restrito a ser usado apenas nesta estrutura. Em vez disso, esse mecanismo de ligação de dados pode ser usado em qualquer lugar e, portanto, neste tutorial, focamos em seu uso como um subsistema independente.
2. Dependências do Maven
A única dependência necessária para ativar a ligação de dados Aegis é:
org.apache.cxf
cxf-rt-databinding-aegis
3.1.8
A versão mais recente deste artefato pode ser encontradahere.
3. Definições de tipo
Esta seção percorre as definições dos três tipos usados para ilustrar o Aegis.
3.1. Course
Esta é a classe mais simples do nosso exemplo, definida como:
public class Course {
private int id;
private String name;
private String instructor;
private Date enrolmentDate;
// standard getters and setters
}
3.2. CourseRepo
CourseRepo é o tipo de nível superior em nosso modelo. Definimos isso como uma interface e não como uma classe para demonstrar como é fácil organizar uma interface Java, o que é impossível no JAXB sem um adaptador personalizado:
public interface CourseRepo {
String getGreeting();
void setGreeting(String greeting);
Map getCourses();
void setCourses(Map courses);
void addCourse(Course course);
}
Observe que declaramos o métodogetCourses com o tipo de retornoMap. Isso é intencional para expressar outra vantagem do Aegis sobre o JAXB. O último não pode empacotar um mapa sem um adaptador personalizado, enquanto o primeiro pode.
3.3. CourseRepoImpl
Esta classe fornece implementação para a 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. Custom Data Binding
Para que a customização entre em vigor, os arquivos de mapeamento XML devem estar presentes no caminho de classe. É necessário que esses arquivos sejam colocados em um diretório cuja estrutura corresponda à hierarquia de pacotes dos tipos Java associados.
Por exemplo, se uma classe de nome totalmente qualificada for chamadapackage.ClassName, seu arquivo de mapeamento associado deve estar dentro do subdiretóriopackage/ClassName no caminho de classe. O nome de um arquivo de mapeamento deve ser igual ao tipo Java associado com um sufixo.aegis.xml anexado a ele.
4.1. Mapeamento deCourseRepo
A interfaceCourseRepo pertence ao pacotecom.example.cxf.aegis, então seu arquivo de mapeamento correspondente é denominadoCourseRepo.aegis.xmle colocado no diretóriocom/example/cxf/aegis no caminho de classe.
No arquivo de mapeamentoCourseRepo, alteramos o nome e o namespace do elemento XML associado à interfaceCourseRepo, bem como o estilo de sua propriedadegreeting:
4.2. Mapeamento de Curso
Semelhante ao tipoCourseRepo, o arquivo de mapeamento da classeCourse é denominadoCourse.aegis.xmle está localizado no diretóriocom/example/cxf/aegis também.
Neste arquivo de mapeamento, instruímos o Aegis a ignorar a propriedadeinstructor da classeCourse durante o empacotamento, de modo que seu valor não esteja disponível no objeto recriado do documento XML de saída:
Aegis' home page é onde podemos encontrar mais opções de personalização.
5. Teste
Esta seção é um guia passo a passo para configurar e executar um caso de teste que ilustra o uso de ligações de dados Aegis.
Para facilitar o processo de teste, declaramos dois campos na classe de teste:
public class exampleTest {
private AegisContext context;
private String fileName = "example.xml";
// other methods
}
Esses campos foram definidos aqui para serem usados por outros métodos desta classe.
5.1. AegisContext Inicialização
Primeiro, um objetoAegisContext deve ser criado:
context = new AegisContext();
Essa instânciaAegisContext é então configurada e inicializada. Aqui está como definimos classes raiz para o contexto:
Set rootClasses = new HashSet();
rootClasses.add(CourseRepo.class);
context.setRootClasses(rootClasses);
Aegis cria um elemento de mapeamento XML para cadaType dentro do objetoSet<Type>. Neste tutorial, definimos apenasCourseRepo como um tipo de raiz.
Agora, vamos definir o mapa de implementação para o contexto para especificar a classe de proxy para a interfaceCourseRepo:
Map, String> beanImplementationMap = new HashMap<>();
beanImplementationMap.put(CourseRepoImpl.class, "CourseRepo");
context.setBeanImplementationMap(beanImplementationMap);
A última configuração do contexto Aegis diz a ele para definir o atributoxsi:type no documento XML correspondente. Este atributo carrega o nome do tipo real do objeto Java associado, a menos que seja substituído pelo arquivo de mapeamento:
context.setWriteXsiTypes(true);
Nossa instânciaAegisContext agora está pronta para ser inicializada:
context.initialize();
Para manter o código limpo, coletamos todos os trechos de código desta subseção em um método auxiliar:
private void initializeContext() {
// ...
}
5.2. Configuração Simples de Dados
Devido à natureza simples deste tutorial, geramos dados de amostra diretamente na memória, em vez de confiar em uma solução persistente. Vamos preencher o repositório do curso usando a lógica de configuração abaixo:
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. Vinculando objetos Java e elementos XML
As etapas que precisam ser tomadas para empacotar objetos Java em elementos XML são ilustradas com o seguinte método auxiliar:
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();
}
Como podemos ver, os objetosAegisWritereAegisType devem ser criados a partir da instânciaAegisContext. O objetoAegisWriter então empacota a instância Java fornecida para a saída especificada.
Neste caso, este é um objetoXMLStreamWriter associado a um arquivo nomeado após o valor do campo de nível de classefileName no sistema de arquivos.
O método a seguir desmarca um documento XML para um objeto Java do tipo especificado:
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;
}
Aqui, um objetoAegisReader é gerado a partir da instânciaAegisContext. O objetoAegisReader então cria um objeto Java a partir da entrada fornecida. Neste exemplo, essa entrada é um objetoXMLStreamReader apoiado pelo arquivo que geramos no métodomarshalCourseRepo descrito acima.
5.4. Asserções
Agora, é hora de combinar todos os métodos auxiliares definidos nas subseções anteriores em um método de teste:
@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
}
Primeiro criamos uma instânciaCourseRepo, em seguida, empacotamos para um documento XML e, por fim, desempacotamos o documento para recriar o objeto original. Vamos verificar se o objeto recriado é o que esperamos:
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());
É claro que com exceção da propriedadeinstructor, todas as demais têm seus valores recuperados, inclusive a propriedadeenrolmentDate com valores do tipoDate. Isso é exatamente o que esperamos, pois instruímos o Aegis a ignorar a propriedadeinstructor ao empacotar objetosCourse.
5.5. Documento XML de saída
Para tornar explícito o efeito dos arquivos de mapeamento do Aegis, mostramos o documento XML sem personalização abaixo:
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!
Compare isso com o caso em que o mapeamento personalizado do Aegis está em ação:
1
2009-02-14T06:31:30+07:00
1
REST with Spring
2
2016-03-01T06:36:40+07:00
2
Learn Spring Security
Você pode encontrar essa estrutura XML emexample.xml, dentro do diretório principal do projeto, após executar o teste definido nesta seção.
Você verá que o atributotype e o namespace do elemento XML correspondente ao objetoCourseRepo mudam de acordo com o que definimos no arquivoCourseRepo.aegis.xml. A propriedadegreeting também é transformada em um atributo, e a propriedadeinstructor dos objetosCourse desaparece conforme o esperado.
Vale ressaltar que, por padrão, o Aegis converte um tipo Java básico no melhor tipo de esquema correspondente, por exemplo, de objetosDate para elementosxsd:dateTime, conforme mostrado neste tutorial. No entanto, podemos alterar essa ligação específica definindo a configuração no arquivo de mapeamento correspondente.
Navegue atéAegis home page se quiser obter mais informações.
6. Conclusão
Este tutorial ilustra o uso da ligação de dados Apache CXF Aegis como um subsistema independente. Ele demonstra como o Aegis pode ser usado para mapear objetos Java para elementos XML e vice-versa.
O tutorial também se concentra em como personalizar comportamentos de ligação de dados.
E, como sempre, a implementação de todos esses exemplos e trechos de código pode ser encontrada emthe GitHub project.