O EsfingeMETADATA é um meta-framework extensível para leitura e validação de metadados, que visa a simplificar e auxiliar principalmente o desenvolvimento de frameworks baseados em metadados.Além disso, também pode ser utilizado em aplicações baseadas em anotações customizadas.
Como regra geral, é necessária a adição dos arquivos JAR do Esfinge Metadata com as seguintes dependências: commons-beanutils-1.9.2.jar commons-collections-3.2.2.jar commons-logging-1.2.jar
O Esfinge Metadata usa como padrão o Maven para gerenciar as dependências. Por isso você deve colocar o artefato do Esfinge Metadata, o maven se encarrega de baixar as dependências que serão usadas.
<dependency>
<groupId>net.sf.esfinge</groupId>
<artifactId>metadata</artifactId>
<version>1.5.6</version>
</dependency>
O Esfinge Metadata possui uma funcionalidade que recupera e localiza anotações. De acordo com configurações feitas nas anotações que estão sendo procuradas, é possível que sejam buscadas em outros lugares relacionados ao elemento, como, por exemplo, dentro de outras anotações que ele possui.
Para localizar os metadado use o método AnnotationFinder.findAnnotation() , este vai retornar uma lista de Anotações.
Exemplo de Localização de Metadatos:Em um primeiro momento criamos a anotação chamada @AnnotationInElement .
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationInElement {
}
Em seguida criamos uma classe que usa a anotação criada anteriormente, neste exemplo chamaremos de ElementWithMetadata .
@AnnotationInElement
public class ElementWithMetadata {
}
E depois criamos a busca por essa anotação no codigo usando o metodo estático AnnotationFinder.findAnnotation() que recebe como parametros um elemento com as Anotações neste caso a classe ElementWithMetadata e a anotação alvo que existe o elemento, neste caso a annotação @AnnotationInElement e tem como retorno uma lista de anotações, em caso falso ele vai retornar uma lista com zero posições.
@Test
public void metadataLocatorTestWithAnnotation(){
List<Annotation> annotationList =
AnnotationFinder.findAnnotation(ElementWithMetadata.class, AnnotationInElement.class); ;
assertTrue(annotationList.size()>0);
assertTrue(annotationList.get(0) instanceof AnnotationInElement);
}
Esta anotação configura quando uma anotação pode ser definida dentro de outra. Quando uma anotação com essa configuração é pesquisada, a implementação da API deve procurá-la dentro de cada uma das anotações do elemento de destino;
Exemplo:
Como exemplo em primeiro lugar, criamos uma anotação contendo @SearchInsideAnnotations que diz que a anotação está dentro de outra anotação. Neste caso a anotação @AnnotationInAnnotation está anotada com a @SearchInsideAnnotations
@Retention(RetentionPolicy.RUNTIME)
@SearchInsideAnnotations
public @interface AnotationInAnotation {
}
Em seguida, criamos uma anotação que contem a anotação criada anteriormente, e tambem colocamos a anotação @SearchInsideAnnotations .
@Retention(RetentionPolicy.RUNTIME)
@AnotationInAnotation
@SearchInsideAnnotations
public @interface AnotationInElement {
}
Criamos uma classe que contem a anotação criada anteriormente.
@AnotationInElement
public class ElementWithMetadata {
}
Por ultimo criamos a busca pela anotação @AnnotationInAnnotation na classe ElementWithMetadata este vai verificar se o @AnnotationInAnnotation existe na classe existe no elemento anotado ou em anotações do elemento anotado.
@Test
public void metadataLocatorTestWithAnnotation() {
List<Annotation> annotationList = AnnotationFinder.findAnnotation(ElementWithMetadata.class, AnotationInAnotation.class);
assertTrue(annotationList.size()>0);
assertTrue(annotationList.get(0) instanceof AnotationInAnotation);
}
Esta anotação configura quando uma anotação pode ser definida no escopo do seu elemento de inclusão. Quando uma anotação com essa configuração é pesquisada em um método, se não for encontrada, a implementação da API deve verificar se ela está presente em sua classe;
Exemplo:
Criemos uma anotação para anotar a classe, contendo a
anotação @SearchOnEnclosingElements, que é a anotação responsavel
por dizer em que nivel a anotação está na classe.
@Retention(RetentionPolicy.RUNTIME)
@SearchOnEnclosingElements
public @interface AnnotationInClass {
}
Em seguida criamos uma classe annotada com a anotação criada anteriormente, com um atributo ou metodo, neste exemplo foi criado com um atributo.
@AnnotationInClass
public class ClassWithAnnotation {
public Object selectedElement;
}
Em seguida executamos o AnnotationFinder.findAnnotation() este caso de teste, vai retornar uma lista de anotações contendo a anotação @AnnotationInClass esta anotação vai retorar um elemento de nivel acima do elemento que está sendo buscado.
@Test
public void testWithAnnotation() throws NoSuchFieldException, SecurityException {
List<Annotation> annotationList =
AnnotationFinder.findAnnotation(ClassWithAnnotation.class.getDeclaredField("selectedElement"),
AnnotationInClass.class);
assertTrue(annotationList.size()>0);
assertTrue(annotationList.get(0) instanceof AnnotationInClass);
}
@SearchOnAbstractions
public @interface AnnotedInterface {
}Uma interface anotada com a anotação criada anteriormente.@AnnotedInterface
public interface InterfaceWithAnnotation {
}Uma classe que implementa a interface que contem a anotaçãopublic class ValidClass implements InterfaceWithAnnotation {
}Em seguida executamos o
AnnotationFinder.findAnnotation()
este caso de teste, vai retornar uma lista de anotações contendo a anotação
@AnnotedInterface
esta anotação vai retorar um elemento anotado na abstração e pode ser utilizado com os outros locators.public class SearchOnAbstractionsTest {
@Test
public void test() {
List<Annotation> annotationList = AnnotationFinder.findAnnotation(ValidClass.class, AnnotedInterface.class);
assertTrue(annotationList.size()>0);
assertTrue(annotationList.get(0) instanceof AnnotedInterface);
}
}
Apesar das anotações poderem ser recuperadas diretamente de um elemento, normalmente as informações contidas nelas são todas colocadas em um objeto que armazena essas informações para a aplicação poder acessar futuramente. Essa classe é chamada de Metadata Container e isso é um padrão de projeto. Essa funcionalidade do Esfinge Metadata faz o mapeamento de anotações para uma classe que armazena essas informações, poupando o trabalho de leitura dos metadados.
@ContainerFor(ContainerTarget.TYPE)
public class ContainerClass {
@ElementName
private String elementName;
@ContainsAnnotation(AnnotationInClass.class)
private boolean existAnnotation;
@ReflectionReference
private Class<?> reference;
@AnnotationProperty(annotation = AnnotationInClass.class, property ="elementInAnnotation")
private String elementInAnnotation;
public String getElementInAnnotation() {
return elementInAnnotation;
}
public void setElementInAnnotation(String elementInAnnotation) {
this.elementInAnnotation = elementInAnnotation;
}
public String getElementName() {
return elementName;
}
public void setElementName(String elementName) {
this.elementName = elementName;
}
public boolean isExistAnnotation() {
return existAnnotation;
}
public void setExistAnnotation(boolean existAnnotation) {
this.existAnnotation = existAnnotation;
}
public Class<?> getReference() {
return reference;
}
public void setReference(Class<?> reference) {
this.reference = reference;
}
}
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationInClass{ {
String elementInAnnotation();
}
Criaremos tambem a classe que vai ser utilizada pelo
AnnotationReader, ela possui as informações da classe
@AnnotationInClass, que neste caso ela passa por parametro uma
string com um valor.@AnnotationInClass(elementInAnnotation="valueOfAnnotation")
public class ClassForReader {
}
@Test
public void testAnnotationsBasic() throws Exception {
ContainerClass container;
AnnotationReader annotationReader = new AnnotationReader();
container = annotationReader.readingAnnotationsTo(ClassForReader.class, ContainerClass.class);
assertTrue(container.isExistAnnotation());
assertEquals(ClassForReader.class.getName(), container.getElementName());
assertEquals(ClassForReader.class, container.getReference());
assertEquals("valueOfAnnotation", container.getElementInAnnotation());
}
| Anotação | Elemento Alvo | Uso |
|---|---|---|
| @ContainsAnnotation | Classe,metodo, field. | Esta anotação de Fields verifica se existe a anotação selecionada no elemento alvo. Ele contem apenas o método value() que retorana se a anotação selecionada existe, esta anotação deve ser usada em tipos primitivos boolean ou classes do tipo Boolean. |
| @AnnotationProperty | Metodo,field. | Esta anotação verifica se existe uma anotação anotada na classe alvo, e alem disso verifica, se esta existe uma propriedade com um nome descrito. |
| @ElementName | Classe,metodo, field. | Esta anotação de field pega o nome do elemento que foi anotado e passa para o field do container. |
| @ReflectionReference | Classe,metodo, field. | Esta anotação de field pega a classe do elemento que foi anotado e passa para o field que foi anotado com esse elemento. |
São anotações usadas para leitura e execução em elementos avançado perante o Metadata Container, que são usados para leitura e processamento de elementos como elementos e metodos.
Em um primeiro momento criaremos um container de classe, armazena as informações de classe, atributos e metodos, foi criado os metodos getAnnotedMethod() e getAnnotedField() para facilitar as criação de caso de teste. Neste caso temos as anotações @ProcessMethods que pega as informações dos metodos e armazena em um container de metodos, e o @AllMethodWith que armazena as anotações anotadas em um container de metodos, enquanto o @ProcessFields armazena todos os elementos de atributos em um container, e o @AllFieldsWith armazena os atributos com determinada anotação em um container de Atributos, todos eles podem ser utilizado como Map, Set e List.@ContainerFor(ContainerTarget.TYPE)
public class ContainerClass {
@ElementName
private String elementName;
@ReflectionReference
private Class<?> reference;
@ProcessMethods
private List<MethodContainer> methodContainer;
@ProcessFields
private List<FieldContainer> fieldContainer;
@AllMethodsWith(AnnotationInElement.class)
private List<MethodContainer> methodWithAnnotation;
@AllFieldsWith(AnnotationInElement.class)
private List<FieldContainer> fieldWithAnnotation;
//METODOS GETS E SETS OMITIDOS
public MethodContainer getAnnotedMethod(String name)
{
for(MethodContainer container : methodContainer){
if(container.getElementName().equals(name)){
return container;
}
}
return null;
}
public FieldContainer getAnnotedField(String name){
for(FieldContainer container : fieldContainer){
if(container.getElementName().equals(name)){
return container;
}
}
return null;
}
}
@ContainerFor(ContainerTarget.FIELDS)
public class FieldContainer {
@ElementName
private String elementName;
@ContainsAnnotation(AnnotationInElement.class)
private boolean existAnnotation;
//METODOS GETS E SETS OMITIDOS
}
@ContainerFor(ContainerTarget.METHODS)
public class MethodContainer {
@ElementName
private String elementName;
@ContainsAnnotation(AnnotationInElement.class)
private boolean existAnnotation;
//METODOS GETS E SETS OMITIDOS
}
Aqui criaremos uma anotação que tem como alvo os metodos e
atributos, pois são eles que estão sendo analizados nesse momento.@Retention(RUNTIME)
@Target({ FIELD, METHOD })
public @interface AnnotationInElement {
}
package net.sf.esfinge.metadata.examples.annotationReader.advanced;
@AnnotationInClass
public class ClassForReader {
private String fieldWinouthAnnotation;
@AnnotationInElement
private String fieldWithAnnotation;
@AnnotationInElement
public void methodWithAnnotation(){
}
public void methodWinouthAnnotation(){
}
}
public class AnnotationReaderTest {
@Test
public void testAnnotationsBasic() throws Exception {
ContainerClass container;
AnnotationReader annotationReader = new AnnotationReader();
container = annotationReader.readingAnnotationsTo(ClassForReader.class, ContainerClass.class);
assertEquals(ClassForReader.class, container.getReference());
MethodContainer methodWithAnnotation = container.getAnnotedMethod("methodWithAnnotation");
assertEquals("methodWithAnnotation", methodWithAnnotation.getElementName());
assertTrue(methodWithAnnotation.isExistAnnotation());
MethodContainer methodWinouthAnnotation = container.getAnnotedMethod("methodWinouthAnnotation");
assertEquals("methodWinouthAnnotation", methodWinouthAnnotation.getElementName());
assertFalse(methodWinouthAnnotation.isExistAnnotation());
FieldContainer fieldWinouthAnnotation = container.getAnnotedField("fieldWinouthAnnotation");
assertEquals("fieldWinouthAnnotation", fieldWinouthAnnotation.getElementName());
assertFalse(fieldWinouthAnnotation.isExistAnnotation());
FieldContainer fieldWithAnnotation = container.getAnnotedField("fieldWithAnnotation");
assertEquals("fieldWithAnnotation", fieldWithAnnotation.getElementName());
assertTrue(fieldWithAnnotation.isExistAnnotation());
}
}
| Anotação | Elemento Alvo | Uso da Anotação |
|---|---|---|
| @ProcessMethods | Container de Classe | Esta anotação de Fields verifica se existe a anotação selecionada no elemento alvo. Ele contem apenas o método value() que retorana se a anotação selecionada existe, esta anotação deve ser usada em tipos primitivos boolean ou classes do tipo Boolean. |
| @ProcessFields | Container de Classe | Esta anotação verifica se existe uma anotação anotada na classe alvo, e alem disso verifica, se esta existe uma propriedade com um nome descrito. |
| @AllMethodWith | Container de Classe | Esta anotação pega as informações dos Methods, e coloca em um contêiner com as informações de Methods, deve ser armazenado em List, Set, Map de contêiner com informações sobre os methods. Ele contem apenas o método value() e somente pega os Methods com aquela anotação definida no mesmo. |
| @AllFieldsWith | Container de Classe | Esta anotação pega as informações dos fields, e coloca em um contêiner de fields, deve ser armazenado em List, Set, Map de contêineres para os fields.Ele contem apenas o método value() e somente pega os fields com aquela anotação definida no mesmo. |
| @ElementProperty | Container de Classe | Pega todos os property da classe e verifica se ele tem alguma anotação. |
São utilizadas para a construção de esquemas personalizados de anotações para a sua aplicação utilizando a estrutura do Esfinge Metadata.
Para eles executarem nescessitam da configAnnotation, que vai uma classe, e do type que vai o ProcessorType
Os Processors são os seguintes, @CustonReader, @ProcessPerMethod, @ProcessPerField.
Os ProcessorType são os seguintes: READER_ADDS_METADATA, READER_IS_PROCESSOR, READER_RETURN_PROCESSOR.
Neste caso o Reader adiciona as informações de processors no container.
Em primeiro Momento criaremos um Container, neste caso vai estar anotado nele o @CustonReader();
@ContainerFor(ContainerTarget.TYPE) @CustomReader(configAnnotation = MyAnnotationReader.class, type = ProcessorType.READER_ADDS_METADATA,
readerConfig = "value") public class Container { private String className; //Get e Set omitidos }
Anotação criada que será executada com o processor.
@Retention(RUNTIME)
public @interface MyAnnotationReader {
Class<? extends InterfaceWithProcessor> value();
}
Anotação que vai na classe que queremos usar com o Esfinge Metadata com o processor.
@Retention(RUNTIME)
@MyAnnotationReader(ExecuteProcessor.class)
public @interface MyAnnotation {
}
Processor que vai ser executado, neste caso vai passar o nome do AnnotatedElement que vai no processor para a execução
public class ExecuteProcessor implements InterfaceWithProcessor {
@Override
public void execute(AnnotatedElement element,Object container) {
((Container)container).setClassName(((Class)element).getName());
}
}
@MyAnnotation
public class MyClass {
}
Metodo main que vai executar o programa, e retornar o nome da classe usando o READER_ADDS_METADATA.
public static void main(String[] args) throws Exception {
AnnotationReader reader = new AnnotationReader();
Container ct = new Container();
ct = reader.readingAnnotationsTo(MyClass.class, Container.class);
System.out.println(ct.getClassName());
}
@ContainerFor(ContainerTarget.TYPE)
public class Container {
@ElementName
private String elementName;
@CustomReader(configAnnotation=PropertyAnnotation.class, type = ProcessorType.READER_IS_PROCESSOR)
private List<PropertyProcessorInterface> interf;
//gets e sets omitidos
}Anotação que usa a PropertyAnnotation,
Definição da property annotation, ela recebe uma classe que extende uma PropetyProcessorInterface, que você vai definir.@Retention(RUNTIME)
@PropertyAnnotation(ExecuteProcessors.class)
public @interface OutraAnnotation {
}
Interface que foi criada para como um template de como deve ser o PropertyAnnotation.
public interface PropertyProcessorInterface {
@ExecuteProcessor
public void execute(Annotation ann, AnnotatedElement ael);
public void print();
}
Classe que foi anotada na anotação @OutraAnnotation, está vai executar os metodos que na Interface estão anotados com @ExecuteProcessor, e ser impresso na tela o que estiver no metodo print().
@ContainerFor(ContainerTarget.ALL)
public class ExecuteProcessors implements PropertyProcessorInterface {
private Annotation ann;
private AnnotatedElement ael;
@ContainsAnnotation(OutraAnnotation.class)
private boolean existAnnotation;
@Override
public void execute(Annotation ann, AnnotatedElement ael) {
this.ann = ann;
this.ael = ael;
}
public boolean isExistAnnotation() {
return existAnnotation;
}
public void setExistAnnotation(boolean existAnnotation) {
this.existAnnotation = existAnnotation;
}
@Override
public void print() {
System.out.println("Elemento "+ael+" anotado com " + ann);
System.out.println("A anotação "+ ann+ " "+existAnnotation);
}
}
A classe que foi anotada com a anotação @OutraAnotação.
@OutraAnnotation
public class ClassWithMetadata {
}Aqui vemos o metodo main() de como é executado o Container com os processors, primeiro instancia um novo Container, e logo em seguida,
public static void main(String args[]) throws Exception {
AnnotationReader ar = new AnnotationReader();
Container ct = new Container();
ct = ar.readingAnnotationsTo(ClassWithMetadata.class, Container.class); for (PropertyProcessorInterface execute :ct.getInterf()) {
execute.print();
}
}
Foi criado um contatainer, neste caso foi criado um container que retorna um tipo de container.
public class ContainerReaderReturn {
@CustomReader(configAnnotation = AnnotationReturn.class, type= ProcessorType.READER_RETURNS_PROCESSOR,readerConfig="value")
private List<ReturnInterface> list;
}
Anotação que vai ser configurada no @CustonReader.
public @interface AnnotationReturn {
Class<? extends ReturnProcessorInterface> value();
}
Interface que foi criada para retornar os dados que vão ser retornados pelo container.
public interface ReturnInterface {
public String returnDados();
}
@AnnotationReturn está vai chamar o reader e o reader vai chamar um metodo que vai popular o container.@Retention(RUNTIME)
@AnnotationReturn(ProcessorAnnotation.class)
public @interface MeuReader {
}ReturnProcessorInterface, está é a classe que vai executar o processor, e retornar a ReturnInterface.public class ProcessorAnnotation implements ReturnProcessorInterface{
@Override
public ReturnInterface toReturn(Annotation an) {
ImprimirAnotacao imprimir = new ImprimirAnotacao(an);
return (ReturnInterface) imprimir;
}
}@MeuReader, está é que vai ser processada pelo Esfinge Metadata.@MeuReader
public class ClassToRead {
}ImprimirAnotação, está é uma implementação da interface ReturnInterface, neste caso ela vai imprimir a anotação que foi passada para a classe.public class ImprimirAnotacao implements ReturnInterface {
private Annotation an;
public ImprimirAnotacao(Annotation an) {
this.an = an;
}
@Override
public String returnDados() {
// TODO Auto-generated method stub
return "Anotação "+an.annotationType();
}
}
public static void main(String[] args) throws Exception {
AnnotationReader ar = new AnnotationReader();
ContainerReaderReturn ct = new ContainerReaderReturn();
ct = ar.readingAnnotationsTo(ClassToRead.class, ct.getClass());
System.out.println(ct.getElementName());
for (ReturnInterface lista : ct.getList()) {
System.out.println(lista.returnDados());
}
}
Em um primeiro momento criaremos o container com uma interface de processor, esta vai ser definida como:
@ContainerFor(ContainerTarget.TYPE)
public class Container{ @ProcessorPerMethod(configAnnotation=ProcessorAnnotation.class,type=ProcessorType.READER_IS_PROCESSOR) Map<Method,ProcessorInterface> map;
//gets e sets omitidos
}
Criaremos a anotação que foi definida em configAnnotation, está será usada para configurar as anotações que serão usadas para dizer qual vai ser o processador da interface que vai ser utilizado no exemplo.
@Retention(RetentionPolicy.RUNTIME)
@SearchInsideAnnotations
@SearchOnEnclosingElements
@SearchOnAbstractions
public @interface ProcessorAnnotation {
Class<? extends ProcessorInterface> value();
}
Criaremos a interface ProcessorInterface, está interface define como será executado o codigo dos processors.
public interface ProcessorInterface {
@ExecuteProcessor
public void processorInitializationFieldAndAnnotedElement(Annotation ann);
public void returnDados();
}
@Entidade, está está anotada com o @ProcessorAnnotation() e é passado uma classe que implementa a interface ProcessorInterface, a anotação @SearchOnAbststractions esta define que a anotação pode estar nos elementos de nivel de Interface ou de Herança.@Retention(RetentionPolicy.RUNTIME)
@SearchOnEnclosingElements
@SearchInsideAnnotations
@SearchOnAbstractions
@ProcessorAnnotation(Processor.class)
public @interface Entidade {
}public class Processor implements ProcessorInterface {
private String field2;
@Override
public void processorInitializationFieldAndAnnotedElement(Annotation ann) {
field2 = ann.annotationType().getName();
}
@Override
public void returnDados() {
System.out.println(field2);
}
}
Classe que vai ser passada para a leitura de anotações, esta classe implementa a Interface DominioInterface, e possui uma implementação do metodo que queremos buscar com o Esfinge Metadata.
public class Dominio implements DominioInterface{
@Override
public void entidade1(){}
}Interface criada com a anotação @Entidade, que por sua vez será buscada no metodo.
public interface DominioInterface {
@Entidade
public void entidade1();
}
Execução do EsfingeMetadata, neste caso ele verifica se a entidade está na classe ou na interface do mesmo.
public static void main(String args[]) throws Exception {
Container container = new Container();
AnnotationReader a1 = new AnnotationReader();
container = a1.readingAnnotationsTo(Dominio.class, container.getClass());
container.map.forEach((key, value) -> {
value.returnDados();
});
}
Exemplo:
Criaremos um container do tipo de field, este por sua vez está anotado com o @ProcessorPerField.
@ContainerFor(ContainerTarget.TYPE)
public class Container{ @ProcessorPerField(configAnnotation=ProcessorAnnotation.class,type=ProcessorType.READER_IS_PROCESSOR) Map<Field,ProcessorInterface> map;
//gets e sets omitidos
}
Criaremos a anotação que foi definida em configAnnotation, está será usada para configurar as anotações que serão usadas para dizer qual vai ser o processador da interface que vai ser utilizado no exemplo.
@Retention(RetentionPolicy.RUNTIME)
@SearchInsideAnnotations
@SearchOnEnclosingElements
public @interface ProcessorAnnotation {
Class<? extends ProcessorInterface> value();
}public interface ProcessorInterface {
@ExecuteProcessor
public void processorInitializationAnnotation(Annotation ann);
public void returnDados();
}@ProcessorAnnotation() definida como ProcessorField.@Retention(RetentionPolicy.RUNTIME)
@SearchOnEnclosingElements
@SearchInsideAnnotations
@SearchOnAbstractions
@ProcessorAnnotation(ProcessorField.class)
public @interface Entidade {
String nome();
}
ProcessorField vai pegar o valor da interface @Entidade que foi passado como parametro,public class ProcessorField implements ProcessorInterface {
private String field1;
@Override
public void processorInitializationAnnotation(Annotation ann) {
field1 =((Entidade)ann).nome();
}
@Override
public void returnDados() {
// TODO Auto-generated method stub
System.out.println(field1);
}
}@Entidade neste caso ele vai retornar a propriedade nome() da @Entidade.public class Dominio{
@Entidade(nome = "nome 1")
private String field1;
@Entidade(nome = "nome 2")
private int field2;
}@Entidade na classe Dominio.public static void main(String args[]) throws Exception {
Container container = new Container();
AnnotationReader a1 = new AnnotationReader();
container = a1.readingAnnotationsTo(Dominio.class, container.getClass());
container.map.forEach((key, value) -> {
value.returnDados();
});
}Nesta seção falaremos sobre os processors, quais as suas funcionalidades e os atributus nescessarios para a a execução dos processors.
| Anotação | Elemento Alvo | Uso da Anotação |
|---|---|---|
| @ExecuteProcessor | Interface que vai ser executada pelos processors | Dis que o metodo da interface será invocado por qualquer um dos processors, e define qual a ordem dos processors a ser executado por predefinição este valor é zero. |
| @ProcessorPerProperty | Container de Classe | Esta anotação processa as das anotações das propriedades, como metodos get ou atributos, quando não sabe-se em qual está sendo utilizado a anotação, passa como parametro o value() que é a interface que está os metodos e o type() que rescebe um dos ProcessorType, tendo como valor padrão ProcessorType.READER_IS_PROCESSOR. |
| @ProcessorPerField | Container de Classe | Esta anotação processa somente as anotações dos Atributos da classe, essa anotação tem que ser passada com os parametros value() que é a interface que está os metodos e o type() que rescebe um dos ProcessorType, tendo como valor padrão ProcessorType.READER_IS_PROCESSOR. |
| @ProcessorPerMethod | Container de Classe | Esta anotação processa somente as anotações dos Metodos da classe, essa anotação tem que ser passada com os parametros value() que é a interface que está os metodos e o type() que rescebe um dos ProcessorType, tendo como valor padrão ProcessorType.READER_IS_PROCESSOR. |
| @CustomReader | Todos os containers | Procura anotações anotadas com a anotação passada como parametro na classe e esta anotação tem que ser passada com os parametros value() que é a interface que está os metodos e o type() que rescebe um dos ProcessorType, tendo como valor padrão ProcessorType.READER_IS_PROCESSOR. |
| Reader | Funcionalidade |
|---|---|
| READER_ADDS_METADATA | Neste caso o reader é o elemento responsável por popular o contêiner demetadados. |
| READER_RETURNS_PROCESSOR | O reader retorna um objeto que foi definido como o processor. |
| READER_IS_PROCESSOR | O reader é o próprio processor. |
O esquema de anotação da própria API pode ser estendido. Se o framework precisar recuperar um metadados da classe que não é suportada pelas anotações API nativas, novas APIs de leitura de metadados podem ser definidas.
Para criar uma extensão do Metadata Reader é preciso:
Criar uma anotação anotada com
@AnnotationReadingConfig()
este arquivo vai dizer qual anotação está sendo passado;
Colocar a anotação
@ValidFieldType()
, esta anotação diz em que tipos ou classes as anotações serão
anotadas. Exemplo:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@AnnotationReadingConfig(AllFieldsWithReadingProcessor.class)
@ValidFieldType({List.class, Set.class,Map.class})
public @interface AllFieldsWith {
Class<? extends Annotation> value();
}
Implemente a interface
AnnotationReadingProcessor
, ele tem essa estrutura:
public interface AnnotationReadingProcessor {
void initAnnotation(Annotation an, Field field) throws AnnotationValidationException;
void read(AnnotatedElement classWithMetadata, Object container, ContainerTarget enumStr) throws AnnotationReadingException;
}
Onde a função
initAnnotation()
é responsável pela inicialização da classe de validação e o
read()
é responsavel pela leitura das anotações:
Esta parte do framework é usada para validar se os elementos de anotação de codigo e verifica a integridade dos dados se estão compativeis com os parametros passados pelas anotações.
Para executar o validador de metadados deve executar os seguintes passos:
import
org.esfinge.metadata.AnnotationValidationException, para chamar
as exeções de validação do EsfingeMetadata:
import
org.esfinge.metadata.validate.MetadataValidator, para declarar
que vai utilizar o EsfingeMetadata para validar o seu codigo:
MetadataValidator.validateMetadataOn();
para procurar as anotações de validações do EsfingeMetadata,
onde o Class<?> é a classe que se deseja
validar:
Nesta seção falaremos sobre a validação de propriedades dos metodos, que são passados pelas anotações.
Em um primeiro momento criaremos uma anotação, que tem as
seguintes anotações, @MinValue() que define o valor minimo que será
passado como parametro da anotação, @MaxValue, o valor maximo que
será passado como parametro da anotação e o @NotNull, que define
que a anotação não pode ser NULL
@SearchOnEnclosingElements
@SearchInsideAnnotations
@Target({ElementType.METHOD, ElementType.TYPE,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PointsToUser {
@MinValue(value=2)
@MaxValue(value=10)
int quantity();
@NotNull
String name();
}
public class ValidAchevimentsPoints {
@PointsToUser(quantity=5,name="ValidPoint")
public void setPoints(){
}
}
Executemos o MetadataValidator.validateMetadaOn() para validar as
anotações, neste caso não vai cair em nenhuma exceção de validação.
@Test
public void validTestCase() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(ValidAchevimentsPoints.class);
}
Neste caso declaramos aqui a anotação com os parametros passados
para um metodo, vemos aqui que o parametro quantitty é 0 e está
abaixo do parametro anotado com a anotação @MinValue.public class InvalidAchevimentsPoints {
@PointsToUser(quantity=0,name="InvalidPoint")
public void setPoints(){
}
}
@MinValue.@Test(expected=AnnotationValidationException.class)
public void invalidTestCase() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(InvalidAchevimentsPoints.class);
}
Aqui está uma lista de todas as anotações que podem ser usadas para a validação de propriedade.
| Anotação | Uso |
|---|---|
| @Unique | Esta anotação de Fields verifica se existe a anotação selecionada no elemento alvo. Ele contem apenas o método value() que retorana se a anotação selecionada existe, esta anotação deve ser usada em tipos primitivos boolean ou classes do tipo Boolean. |
| @NotNull | Esta anotação verifica se existe uma anotação anotada na classe alvo, e alem disso verifica, se esta existe uma propriedade com um nome descrito. |
| @MinValue | Esta anotação de field pega o nome do elemento que foi anotado e passa para o field do container. |
| @MaxValue | Esta anotação de field pega a classe do elemento que foi anotado e passa para o field que foi anotado com esse elemento. |
| @RefersTo | Este validador indica que o valor do atributo de uma anotação deve referir-se (ser igual) ao valor do atributo de uma outra anotação em algum lugar no programa. O validador define os seguintes atributos: annotation(): A anotação alvo ao qual o atributo atual se refere. attributeValue(): O valor do atributo da anotação alvo ao qual o atributo atual se refere. |
Aqui são definidas as anotações de contexto, quais as anotações são permitidas ou proibidas de serem usadas.
Neste caso criaremos uma anotação denominada
@PointsToUserValid() que é a classe que contem as anotações a serem
validadas, eles tem anotada as anotações @NeedToHave que verifica
se esta anotação contem a anotação passada como parametro, e a
@Prohibits que diz que a anotação não deve ser passada em nenhum
parametro da anotação.
@SearchOnEnclosingElements
@SearchInsideAnnotations
@Target({ElementType.METHOD, ElementType.TYPE,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@NeedsToHave(MaxValue.class)
@Prohibits(MinValue.class)
public @interface PointsToUserValid {
@MaxValue(value=10)
int quantity();
@NotNull
String name();
}
Criaremos uma anotação que tem um metodo que vai ser validado pela anotação criada anteriormente.
public class ValidAchevimentsPoints {
@PointsToUserValid(quantity=5,name="ValidPoint")
public void setPoints(){
}
}
Executemos o MetadataValidator.validateMetadataOn(), neste caso estará executando corretamente.
@Test
public void validTestCase() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(PointsToUserValid.class);
}
Já neste caso, observamos que tem tambem a anotação
@MinValue, que está proibida com a anotação @Prohibits(),
@SearchOnEnclosingElements
@SearchInsideAnnotations
@Target({ElementType.METHOD, ElementType.TYPE,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@NeedsToHave(MaxValue.class)
@Prohibits(MinValue.class)
public @interface PointsToUserInvalidNeedToHave {
@MinValue(value=2)
@MaxValue(value=10)
int quantity();
@NotNull
String name();
}
Tambem foi criada uma classe com a anotação criada anteriormente.
public class AchevimentsPointsWithNeedToHaveExeption {
@PointsToUserInvalidProhibits(quantity=0,name="InvalidPoint")
public void setPoints()
{
}
}
Neste caso quando executar o
MetadataValidator.validateMetadataOn() vai cair em uma exceção pois
não está seguindo a regra do @Prohibits.
@Test(expected=AnnotationValidationException.class)
public void needToHaveTestCase() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(AchevimentsPointsWithNeedToHaveExeption.class);
}
Aqui está uma lista de todas as anotações que podem ser usadas para a validação de contexto.
| Anotação | Uso |
|---|---|
| @Prohibits | Este validador estabelece que se um elemento de código é anotado com a meta-anotação atual, então, ela proibirá a utilização de outra anotação informada. Este validator é definido por um único atributo value() informando a classe da anotação a ser proibida. |
| @NeedsToHave | Este validador é o opostodo @Prohibits.Ele requer que a meta-anotação atual deve ocorrer em elementos que contenham a anotação informada no value(). |
@OneAnnotationWithStaticFieldOnly, ela vai verificar se o atributo é
estatico.@SearchInsideAnnotations
@SearchOnEnclosingElements
@StaticFieldOnly
@Retention(RetentionPolicy.RUNTIME)
public @interface OneAnnotationWithStaticFieldOnly {
}
Uma classe com o atributo estatico
name
anotado com @OneAnnotationWithStaticFieldOnly
public class StaticFieldValid {
@OneAnnotationWithStaticFieldOnly
private static String name;
}
Criaremos aqui um caso de teste valido, pois o atributo anotado criado anteriormente, é estatico.@Test
public void testValidModyfers() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(StaticFieldValid.class);
}
Aqui estamos vendo um caso com erro, pois o elemento anotado com a anotação @OneAnnotationWithStaticFieldOnly, não é um atributo estatico e portanto vai ocasionar uma exeção.
public class StaticFieldInvalid{
@OneAnnotationWithStaticFieldOnly
protected String nameInvalid;
}
Criaremos aqui um caso de teste que vai retornar a exceção AnnnotationValidationExeption, pois possui um atributo invalido na classe StaticFieldInvalid.
@Test(expected=AnnotationValidationException.class)
public void testInvalidModyfers() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(StaticFieldInvalid.class);
}
| Anotação | Uso |
|---|---|
| @FinalFieldOnly | Esta meta-anotação indica que o campo que utiliza a anotação anotada precisa ter o modificador final. |
| @StaticFieldOnly | Esta meta-anotação indica que o campo que utiliza a anotação anotada precisa ter o modificador static. |
| @TransientFieldOnly | Esta meta-anotação indica que o campo que utiliza a anotação anotada precisa ter o modificador transient. |
| @VolatileFieldOnly | Esta anotação de field pega a classe do elemento que foi anotado e passa para o field que foi anotado com esse elemento. |
| @InstanceFieldOnly | Esta meta-anotação indica que o campo que utiliza a anotação anotada NÃO pode ter o modificador static. |
Nesta seção falaremos sobre as validações de visibilidade, e verificamos se os atributos são visiveis ou proibidos no codigo.
Para esse exemplo criaremos uma anotação chamada @OneAnnotationWithFieldVisibilityRequired, ela contem a seguinte anotação do EsfingeMetadata. @FieldVisibilityRequired, que tem como parametro a visibilidade do atributo, neste caso o atributo deve ser privado.
@SearchInsideAnnotations
@SearchOnEnclosingElements
@FieldVisibilityRequired(itNeedsToHaveThisVisibility = "private")
@Retention(RetentionPolicy.RUNTIME)
public @interface OneAnnotationWithFieldVisibilityRequired {
}
Criaremos uma classe e anotamos o atributo name, com a anotação criada anteriormente, e vemos que o tipo do atributo é o mesmo da que foi passado na anotação criada anteriormente.
public class VisibilityRequeridValid {
@OneAnnotationWithFieldVisibilityRequired
private String name;
}
Executamos um caso de teste, para verificar se o EsfingeMetadata valida corretamente esse parametro.
@Test
public void testValidVisibility() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(VisibilityRequeridValid.class);
}
Neste caso temos um elemento com visibilidade protected, este difere da visibilidade requirida na validação.
public class VisibilityRequeridInvalid {
@OneAnnotationWithFieldVisibilityRequired
protected String name;
}
Aqui está o exemplo de validação com anotação invalida, neste caso está esperado a exceção AnnotationValidationExcepition, pois neste caso está esperando um atributo com a visibilidade private mas está recebendo um atributo com a visibilidade protected.
@Test(expected=AnnotationValidationException.class)
public void testInvalidVisibility() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(VisibilityRequeridInvalid.class);
}
Aqui está a listas de anotações que podem ser utilizados para validação de visibilidade.
| Anotação | Uso |
|---|---|
| @FieldVisibilityForbidden | Esta meta-anotação indica que o campo que utiliza a anotação anotada NÃO pode ter a visibilidade passada no parâmetro itCannotHaveThisVisibility. |
| @FieldVisibilityRequired | Esta meta-anotação indica que o campo que utiliza a anotação anotada precisa ter a visibilidade passada no parâmetro itNeedsToHaveThisVisibility. |
Aqui falaremos sobre validadores de tipo para atributos, nesta seção tem somente a anotação @ValidFieldTypes.
Criaremos uma anotação anotada com @ValidFIeldType, que recebe como parametro listValidTypes que é um array de classes, este define quais tipos são permitidos de existir no atributo, neste caso é String, List.
@SearchInsideAnnotations
@SearchOnEnclosingElements
@ValidFieldTypes(listValidTypes = {String.class, List.class})
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface OneAnnotationWithValidFieldTypes {
}
Aqui criamos uma classe com o atributo a ser validado, neste caso vai ser validado pois o tipo que está sendo passado é do mesmo tipo que foi passado previamente para a anotação @OneAnnotationWithValidFieldTypes.
public class TypeFieldValid {
@OneAnnotationWithValidFieldTypes
private String name;
}
Neste caso vai ocorrer uma exceção pois o atributo não passa nenhum dos tipos definido na anotaçaõ @OneAnnotationWithValidFieldTypes.
public class TypeFieldInvalid{
@OneAnnotationWithValidFieldTypes
private int invalid;
}
Aqui tem um exemplo com os dois comportamentos que podem ocorrer na execução dessa validação, o validTypeExamples() ocorre quando não tem nenhum erro de validação de propriedades, enquanto o invalidTypeExamples() vai disparar a exeção que esta sendo experada.
public class TypeExamples {
@Test
public void validTypeExamples() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(TypeFieldValid.class);
}
@Test(expected=AnnotationValidationException.class)
public void invalidTypeExamples() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(TypeFieldInvalid.class);
}
}
| Anotação | Uso |
|---|---|
| @ValidFieldTypes | Esta meta-anotação indica que o campo que utiliza a anotação anotada precisa ter um tipo correspondente ao passado no parâmetro listValidTypes. |
Nesta seção vai ser falado sobre a validação de contexto por metodos, e quais são as anotações que foram criadas para o uso deles, alem de um exemplo basico de uso.
Em um primeiro momento criaremos uma anotação chamada @OneAnnotationWithFinalMethodOnly, que contem a anotação @FinalMethodOnly que vai ser responsavel por dizer que o metodo que essa classe está anotada é final.
@FinalMethodOnly
@Retention(RetentionPolicy.RUNTIME)
public @interface OneAnnotationWithFinalMethodOnly {
}
Exemplo de um metodo anotado com a anotação criada anteriormente, este é a versão correta de dizer que um metodo é valido, conforme o parametro criado anteriormente.
public class FinalMethodOnlyValid {
@OneAnnotationWithFinalMethodOnly
public final void finalValid(){
}
}
Exemplo de um metodo anotado com a anotação criada anteriormente, este é invalido, pois o metodo anotado não é do tipo final.
import net.sf.esfinge.metadata.examples.validate.method.test.OneAnnotationWithFinalMethodOnly;
public class FinalMethodOnlyInvalid {
@OneAnnotationWithFinalMethodOnly
public void finalInvalid(){}
}
Exemplo de caso de teste para validação dos metodos, o caso modifersValid() contem a anotação valida, enquanto o modifersInvalid() contem a anotação que retorna a exceção requirida.
public class ModifersTest
@Test
public void modifersValid() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(FinalMethodOnlyValid.class);
}
@Test(expected=AnnotationValidationException.class)
public void modifersInvalid() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(FinalMethodOnlyInvalid.class);
}
}
Aqui a lista de anotações que podem ser usados para validação por modificadores de Metodos.
| Anotação | Uso |
|---|---|
| @FinalMethodOnly | Esta meta-anotação indica que o método que utiliza a anotação anotada precisa ter o modificador final. |
| @StaticMethodOnly | Esta meta-anotação indica que o método que utiliza a anotação anotada precisa ter o modificador static. |
| @SyncronizedMethodOnly | Esta meta-anotação indica que o método que utiliza a anotação anotada precisa ter o modificador synchronized. |
| @StrictfpMethodOnly | Esta meta-anotação indica que o método que utiliza a anotação anotada precisa ter o modificador strictfp. |
| @InstanceMethodOnly | Esta meta-anotação indica que o método que utiliza a anotação anotada NÃO pode ter o modificador static. |
Exemplo de @MethodVisibilityForbidden, neste caso dis qual o tipo que o metodo não pode ser, neste caso o metodo não pode ser privado.
@MethodVisibilityForbidden(itCannotHaveThisVisibility = "private")
@Retention(RetentionPolicy.RUNTIME)
public @interface OneAnnotationWithMethodVisibilityForbidden {
}
Aqui um exemplo valido, repare que neste caso o metodo não é privado.
public class VisibilityForbidenValid {
@OneAnnotationWithMethodVisibilityForbidden
public double someCalc(double a, double b){
return Math.pow(( a * b ) * 4.2, 5);
}
}
Exemplo que pode ocorrer uma exceção, o metodo é privado.
public class VisibilityForbidenInvalid {
@OneAnnotationWithMethodVisibilityForbidden
private double someCalc(double a, double b){
return Math.pow(( a * b ) * 4.2, 5);
}
}
Aqui está um caso de teste que faz a verificação das classes criadas para a validação, repare que o testVisibilityValid() valida a anotação, enquanto o testVisibilityInvalid() retorna uma anotação que contem exceção apresentada.
public class ValidateVisibility {
@Test
public void testVisibilityValid() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(VisibilityForbidenValid.class);
}
@Test(expected=AnnotationValidationException.class)
public void testVisibilityInvalid() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(VisibilityForbidenInvalid.class);
}
}
| Anotação | Uso |
|---|---|
| @MethodVisibilityForbidden | Esta meta-anotação indica que o método que utiliza a anotação anotada NÃO pode ter a visibilidade passada no parâmetro itCannotHaveThisVisibility. |
| @MethodVisibilityRequired | Esta meta-anotação indica que o método que utiliza a anotação anotada precisa ter a visibilidade passada no parâmetro itNeedsToHaveThisVisibility. |
@MethodNamingConvention, esta recebe como parametro um REGEX com o nome do metodo.@MethodNamingConvention(regexNamingConvencion = "^get")
@Retention(RetentionPolicy.RUNTIME)
public @interface OneAnnotationWithMethodNamingConvention {
}
Criaremos uma classe valida usando a anotação criada anteriormente, neste caso vai executar com sucesso pois o metodo começa com o REGEX ^get.
public class NamingConvectionTrue {
@OneAnnotationWithMethodNamingConvention
public void getName(){
}
}
Neste caso, não vai estar correto pois o nome do metodo não contem o REGEX ^get.
public class NamingConvectionFalse {
@OneAnnotationWithMethodNamingConvention
public void setName(){
}
}
Aqui está o codigo de teste, ele vai retornar as exeções de codigo para o caso que não satisfaz a validação, e não vai retornar nada no codigo que satisfaz a validação.public class NamingConvectionTest {
@Test
public void NamingConvectionValid() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(NamingConvectionTrue.class);
}
@Test(expected=AnnotationValidationException.class)
public void NamingConvectionInvalid() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(NamingConvectionFalse.class);
}
}
| Anotação | Uso |
|---|---|
| @MethodNammingConvention | Esta meta-anotação indica que o método que utiliza a anotação anotada precisa ter um nome de método de acordo com a expressão regular passada no parâmetro regexNamingConvencion. |
Em um primeiro momento criaremos uma anotação que tem anotada o @ValidNumbersOfParameters, neste caso o valor é 2.
@ValidNumberOfParameters(numberOfParameters = 2)
@Retention(RetentionPolicy.RUNTIME)
public @interface OneAnnotationWithValidNumberOfParameters {
}
Criaremos uma classe com os numeros parametros validos pela anotação criada anteriormente.
public class ParameterForbidenValid {
@OneAnnotationWithValidNumberOfParameters
public double someCalc(double a, double b){
return Math.pow(( a * b ) * 4.2, 5);
}
}
Aqui vai ser criada uma classe com os parametros invalidos.
public class ParameterForbidenInvalid {
@OneAnnotationWithValidNumberOfParameters
private strictfp double someCalc(double a){
return Math.pow(( a ) * 4.2, 5);
}
}
Criaremos o caso de teste para a verificação do uso com anotação valida e anotação invalida.
public class ParameterVisibility {
@Test
public void testVisibilityValid() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(ParameterForbidenValid.class);
}
@Test(expected=AnnotationValidationException.class)
public void testVisibilityInvalid() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(ParameterForbidenInvalid.class);
}
}
Listas de anotação de validação por parametros.
| Anotação | Uso |
|---|---|
| @NoParameterMethodOnly | Esta meta-anotação indica que o método que utiliza a anotação anotada NÃO pode ter parâmetros. |
| @ValidNunberOfParameters | Esta meta-anotação indica que o método que utiliza a anotação anotada precisa ter a quantidade de parâmetros passado no parâmetro numberOfParameters. |
| @ValidMethodParameterTypes | Esta meta-anotação indica que o método que utiliza a anotação anotada precisa ter os parâmetros correspondentes ao que foi passado no parâmetro validParameters, onde os parâmetros válidos são indicados pela anotação @Parameters no parâmetro parameters. |
@ValidMethodReturn, que é responsavel por dizer qual o retorno esperado para determinado metodo, neste caso do exemplo é int.class ou Integer.class.@ValidMethodReturn(validTypesToReturn = {int.class, Integer.class})
@Retention(RetentionPolicy.RUNTIME)
public @interface OneAnnotationWithValidMethodReturn {
}
Criaremos uma classe com um metodo anotado com a anotação criada anteriormente, neste caso é valido pois está retornando um tipo inteiro.
public class ReturnValid {
@OneAnnotationWithValidMethodReturn
public int someCalc(int a, int b){
return a+b;
}
}
Criaremos uma classe com um metodo anotado com a anotação criada anteriormente, neste caso é invalido pois está retornando uma string.
public class ReturnInvalid {
@OneAnnotationWithValidMethodReturn
private String someCalc(double a){
return "INVALID";
}
}
Aqui a execução do caso de teste, repare que quando se testa o caso invalido ele cai em uma exceção de AnnotationValidationException.
public class ParameterVisibility {
@Test
public void testVisibilityValid() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(ReturnValid.class);
}
@Test(expected=AnnotationValidationException.class)
public void testVisibilityInvalid() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(ReturnInvalid.class);
}
}
| Anotação | Uso |
|---|---|
| @ForbiddenMethodReturn | Esta meta-anotação indica que o método que utiliza a anotação anotada NÃO pode ter o retorno passado no parâmetro invalidTypesToReturn. |
| @ValidMethodReturn | Esta meta-anotação indica que o método que utiliza a anotação anotada precisa ter o retorno passado no parâmetro validTypesToReturn. |
Para criar uma extensão para o validador de metadados é necessário:
Criar uma anotação anotada com:
@ToValidateProperty()
Exemplo:
@ToValidateProperty(validationClass = UniqueAnnotationValidator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Unique {
}
Implemente o AnnotationPropertyValidator, ele tem essa
estrutura:
public interface AnnotationPropertyValidator {
public void initialize(Annotation self);
public void validate(Annotation annotationOnElement, AnnotatedElement annotatedWithMainAnnotation, Method annotatedWithValidation, Object annotationPropertyValue) throws AnnotationValidationException;
}
Onde o método
initialize()
é responsável pela inicialização da classe de validação e o
validate()
é responsavel pela validação;
Segue um exemplo da implementação da Anotação Unique.
public class UniqueAnnotationValidator implements AnnotationPropertyValidator {
private List<Annotation> annotationList = new ArrayList<Annotation>();
@Override
public void initialize(Annotation self) {
}
@Override
public void validate(Annotation annotationOnElement,
AnnotatedElement annotatedWithMainAnnotation,
Method annotatedWithValidation, Object annotationPropertyValue)
throws AnnotationValidationException {
Method[] methods = annotationOnElement.annotationType().getMethods();
for (Method m : methods) {
for(Annotation an : m.getAnnotations()){
if(an instanceof Unique){
annotationList.add(an);
}
}
}
if(annotationList.size() > 1){
throw new
AnnotationValidationException("The @"+annotationOnElement.annotationType().getSimpleName()+" must have only one field annotated with @Unique!");
}else if (annotationList.size() == 0 ) {
throw new AnnotationValidationException("The annotation @"
+ Unique.class.getSimpleName().toString()
+ " was not found");
}
}
}
Aqui criaremos um exemplo de uso desta anotação, em um primeiro momento criaremos uma anotação, esta anotação possue dois atributos anotado com @Unique, sendo que neste caso somente deve ter uma anotação anotada com @Unique.
@SearchOnEnclosingElements
@SearchInsideAnnotations
@Target({ElementType.METHOD,ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PointsToUser {
@Unique
int quantity();
@Unique
String name();
}Aqui foi criado um caso de teste que vai retornar a AnnotationValidationExeption, pois a anotação a ser validada é invalida de acordo com o funcionamento da anotação @Unique.@Test(expected = AnnotationValidationException.class) public void VerifiyIsUnique() throws AnnotationValidationException, IllegalArgumentException, IllegalAccessException,
NoSuchFieldException, SecurityException { MetadataValidator.validateMetadataOn(Classe.class); }
Tambem no Metadata Validator possui outro ponto para a extenção do Metadata Validator a Interface AnnotationValidator.
Aqui um exemplo de uso desta anotação.
Em primeiro momento criaremos uma anotação anotada com@ToValidate(), esta tem como parametro a implementação da interface AnnotationValidator.@ToValidate(value = ProhibitsAnnotationValidator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Prohibits {
public Class<? extends Annotation> value();
}
Em seguida criaremos o a implementação do validador.
public class ProhibitsAnnotationValidator implements AnnotationValidator{
protected Class<? extends Annotation> whatItNeedsToProhibits;
@Override
public void initialize(Annotation self) {
Prohibits ntp = (Prohibits) self;
whatItNeedsToProhibits = ntp.value();
}
@Override
public void validate(Annotation toValidate, AnnotatedElement annotated) throws AnnotationValidationException {
if(AnnotationFinder.findAnnotation(annotated, whatItNeedsToProhibits).size() > 0){
throw new AnnotationValidationException("The annotation it needs to prohibits was found = "
+ whatItNeedsToProhibits.toString());
}
}
}
Criamos então duas anotações que implementa a anotação @Prohibits, esta recebe uma anotação que [não deve ser colocada de nenhuma maneira no projeto, cada uma proibe a outra.
@Prohibits(OptimizeExecution01.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface Logging01 {
}
@SearchOnEnclosingElements
@SearchInsideAnnotations
@Prohibits(Logging01.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface OptimizeExecution01 {
}
Em seguida criaremos o teste das classes, vai acionar a AnnotationValidationExeption pois esta possui as anotações proibidas de executar.
public class OrderProcessing02 {
@Logging01
@OptimizeExecution01
public void registerPurchase(Purchase p) {
}
}
@Test(expected = AnnotationValidationException.class)
public void CTWithExeption() throws AnnotationValidationException {
MetadataValidator.validateMetadataOn(OrderProcessing02.class);
}
Apoio